Reorganise the file structure into a project tree

This commit is contained in:
2011-03-11 12:58:50 +00:00
parent 58b1691638
commit 3686aa7523
226 changed files with 7 additions and 5 deletions

0
app/sso/__init__.py Normal file
View File

39
app/sso/admin.py Normal file
View File

@@ -0,0 +1,39 @@
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from sso.models import Service, ServiceAccount, SSOUser, SSOUserNote
class ServiceAdmin(admin.ModelAdmin):
list_display = ('name', 'url', 'api', 'active')
search_fields = ['name']
list_filter = ('active',)
class ServiceAccountAdmin(admin.ModelAdmin):
list_display = ('service', 'service_uid', 'user', 'active')
search_fields = ['service_uid', 'user__username']
list_filter = ('service', 'active')
class SSOUserProfileInline(admin.StackedInline):
model = SSOUser
fk_name = 'user'
max_num = 1
# Define a new UserAdmin class
class SSOUserAdmin(UserAdmin):
inlines = [SSOUserProfileInline, ]
class SSOUserNoteAdmin(admin.ModelAdmin):
list_display = ('user', 'note', 'date_created', 'created_by')
search_fields = ['user__username']
admin.site.register(Service, ServiceAdmin)
admin.site.register(ServiceAccount, ServiceAccountAdmin)
admin.site.unregister(User)
admin.site.register(User, SSOUserAdmin)
admin.site.register(SSOUserNote, SSOUserNoteAdmin)

27
app/sso/backends.py Normal file
View File

@@ -0,0 +1,27 @@
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.models import User
from hashlib import sha1
class SimpleHashModelBackend(ModelBackend):
supports_anonymous_user = False
supports_object_permissions = False
supports_inactive_user = False
def authenticate(self, username=None, password=None):
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
if '$' in user.password:
if user.check_password(password):
user.password = sha1(password).hexdigest()
user.save()
return user
else:
if user.password == sha1(password).hexdigest():
return user
return None

110
app/sso/forms.py Normal file
View File

@@ -0,0 +1,110 @@
import re
from django import forms
from django.contrib.auth.models import User
from django.conf import settings
from utils import installed
from eve_api.models import EVEAccount, EVEPlayerCharacter, EVEPlayerCorporation
from sso.models import ServiceAccount, Service
from registration.forms import RegistrationForm
class RegistrationFormUniqueEmailBlocked(RegistrationForm):
"""
Subclass of ``RegistrationForm`` which disallows registration from certain
domains and also makes sure that the email address is unique in the DB
"""
def clean_email(self):
"""
Check the supplied email address against a list of known free
webmail domains.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError("This email address is already in use. Please supply a different email address.")
return self.cleaned_data['email']
email_domain = self.cleaned_data['email'].split('@')[1]
if email_domain in settings.BANNED_EMAIL_DOMAINS:
raise forms.ValidationError("Your email provider (%s) is banned from registering, please use a different address.")
return self.cleaned_data['email']
def UserServiceAccountForm(user):
""" Generate a Service Account form based on the user's permissions """
services = Service.objects.filter(groups__in=user.groups.all(), active=1).exclude(id__in=ServiceAccount.objects.filter(user=user).values('service')).distinct()
chars = EVEPlayerCharacter.objects.filter(eveaccount__user=user)
class ServiceAccountForm(forms.Form):
""" Service Account Form """
character = forms.ModelChoiceField(queryset=chars, required=True, empty_label=None)
service = forms.ModelChoiceField(queryset=services, required=True, empty_label=None)
def __init__(self, *args, **kwargs):
super(ServiceAccountForm, self).__init__(*args, **kwargs)
if not settings.GENERATE_SERVICE_PASSWORD:
self.password = forms.CharField(widget=forms.PasswordInput, label="Password")
self.fields['password'] = self.password
def clean(self):
form_data = self.cleaned_data
service = form_data.get('service')
character = form_data.get('character')
if not service or not character:
raise forms.ValidationError('Error while processing the form, please try again')
service_groups = service.groups.all()
# If the service's assigned groups are linked to corps, do a character/corp check
if len(EVEPlayerCorporation.objects.filter(group__in=service_groups)):
corp_group = character.corporation.group
if character.corporation.alliance:
alliance_group = character.corporation.alliance.group
else:
alliance_group = None
if not (corp_group in service_groups or alliance_group in service_groups):
raise forms.ValidationError("%s is not in a corporation or alliance allowed to register for %s" % (self.cleaned_data['character'].name, self.cleaned_data['service']))
return self.cleaned_data
return ServiceAccountForm
class ServiceAccountResetForm(forms.Form):
""" Password reset form for Services """
def __init__(self, *args, **kwargs):
super(ServiceAccountResetForm, self).__init__(*args, **kwargs)
if not settings.GENERATE_SERVICE_PASSWORD:
self.password = forms.CharField(widget=forms.PasswordInput, label="Password")
self.fields['password'] = self.password
class UserLookupForm(forms.Form):
""" User Lookup Form """
def __init__(self, *args, **kwargs):
super(UserLookupForm, self).__init__(*args, **kwargs)
choices = [(1, "Auth Username"),
(2, "Character"),
(4, "Email Address"),
(5, "EVE API User ID"), ]
if installed('reddit'):
choices.append((3, "Reddit ID"))
self.fields['type'] = forms.ChoiceField(label=u'Search type', choices=choices)
self.fields['username'] = forms.CharField(label=u'User ID', max_length=64)
class APIPasswordForm(forms.Form):
""" API Password reset form """
password = forms.CharField(widget=forms.PasswordInput, label="Password")

52
app/sso/middleware.py Normal file
View File

@@ -0,0 +1,52 @@
class IGBMiddleware(object):
"""
Middleware to detect the EVE IGB
"""
def process_request(self, request):
request.is_igb = False
request.is_igb_trusted = False
header_map = [
('HTTP_EVE_SERVERIP', 'eve_server_ip'),
('HTTP_EVE_CHARNAME', 'eve_charname'),
('HTTP_EVE_CHARID', 'eve_charid'),
('HTTP_EVE_CORPNAME', 'eve_corpname'),
('HTTP_EVE_CORPID', 'eve_corpid'),
('HTTP_EVE_ALLIANCENAME', 'eve_alliancename'),
('HTTP_EVE_ALLIANCEID', 'eve_allianceid'),
('HTTP_EVE_REGIONNAME', 'eve_regionid'),
('HTTP_EVE_CONSTELLATIONNAME', 'eve_constellationname'),
('HTTP_EVE_SOLARSYSTEMNAME', 'eve_systemname'),
('HTTP_EVE_STATIONNAME,' 'eve_stationname'),
('HTTP_EVE_STATIONID,' 'eve_stationid'),
('HTTP_EVE_CORPROLE,' 'eve_corprole'),
]
if request.META.has_key('HTTP_EVE_TRUSTED'):
request.is_igb = True
if request.META.get('HTTP_EVE_TRUSTED') == 'Yes':
request.is_igb_trusted = True
for header, map in header_map:
if request.META.get(header, None):
setattr(request, map, request.META.get(header, None))
from sso.models import SSOUserIPAddress
from datetime import datetime
class IPTrackingMiddleware(object):
"""
Middleware to track user's IPs and insert them into the database
"""
def process_request(self, request):
if request.user and not request.user.is_anonymous():
ip, created = SSOUserIPAddress.objects.get_or_create(user=request.user, ip_address=request.META['REMOTE_ADDR'])
if not created:
ip.last_seen = datetime.utcnow()
ip.save()

View File

@@ -0,0 +1,139 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'SSOUser'
db.create_table('sso_ssouser', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='profile', unique=True, to=orm['auth.User'])),
('default_service_passwd', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
('default_service_username', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
('website', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
('aim', self.gf('django.db.models.fields.CharField')(max_length=64, blank=True)),
('msn', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
('icq', self.gf('django.db.models.fields.CharField')(max_length=15, blank=True)),
('xmpp', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
))
db.send_create_signal('sso', ['SSOUser'])
# Adding model 'Service'
db.create_table('sso_service', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
('url', self.gf('django.db.models.fields.CharField')(max_length=200, blank=True)),
('active', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
('api', self.gf('django.db.models.fields.CharField')(max_length=200)),
('settings_json', self.gf('jsonfield.fields.JSONField')(blank=True)),
))
db.send_create_signal('sso', ['Service'])
# Adding M2M table for field groups on 'Service'
db.create_table('sso_service_groups', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('service', models.ForeignKey(orm['sso.service'], null=False)),
('group', models.ForeignKey(orm['auth.group'], null=False))
))
db.create_unique('sso_service_groups', ['service_id', 'group_id'])
# Adding model 'ServiceAccount'
db.create_table('sso_serviceaccount', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('service', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['sso.Service'])),
('service_uid', self.gf('django.db.models.fields.CharField')(max_length=200)),
('active', self.gf('django.db.models.fields.BooleanField')(default=True, blank=True)),
))
db.send_create_signal('sso', ['ServiceAccount'])
def backwards(self, orm):
# Deleting model 'SSOUser'
db.delete_table('sso_ssouser')
# Deleting model 'Service'
db.delete_table('sso_service')
# Removing M2M table for field groups on 'Service'
db.delete_table('sso_service_groups')
# Deleting model 'ServiceAccount'
db.delete_table('sso_serviceaccount')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sso.service': {
'Meta': {'object_name': 'Service'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'api': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'settings_json': ('jsonfield.fields.JSONField', [], {'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'sso.serviceaccount': {
'Meta': {'object_name': 'ServiceAccount'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sso.Service']"}),
'service_uid': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'sso.ssouser': {
'Meta': {'object_name': 'SSOUser'},
'aim': ('django.db.models.fields.CharField', [], {'max_length': '64', 'blank': 'True'}),
'default_service_passwd': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'default_service_username': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'icq': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'msn': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"}),
'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'xmpp': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
}
}
complete_apps = ['sso']

View File

@@ -0,0 +1,126 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'SSOUser.website'
db.delete_column('sso_ssouser', 'website')
# Deleting field 'SSOUser.xmpp'
db.delete_column('sso_ssouser', 'xmpp')
# Deleting field 'SSOUser.default_service_username'
db.delete_column('sso_ssouser', 'default_service_username')
# Deleting field 'SSOUser.aim'
db.delete_column('sso_ssouser', 'aim')
# Deleting field 'SSOUser.msn'
db.delete_column('sso_ssouser', 'msn')
# Deleting field 'SSOUser.default_service_passwd'
db.delete_column('sso_ssouser', 'default_service_passwd')
# Deleting field 'SSOUser.icq'
db.delete_column('sso_ssouser', 'icq')
# Adding field 'SSOUser.api_service_password'
db.add_column('sso_ssouser', 'api_service_password', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
def backwards(self, orm):
# Adding field 'SSOUser.website'
db.add_column('sso_ssouser', 'website', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
# Adding field 'SSOUser.xmpp'
db.add_column('sso_ssouser', 'xmpp', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
# Adding field 'SSOUser.default_service_username'
db.add_column('sso_ssouser', 'default_service_username', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
# Adding field 'SSOUser.aim'
db.add_column('sso_ssouser', 'aim', self.gf('django.db.models.fields.CharField')(default='', max_length=64, blank=True), keep_default=False)
# Adding field 'SSOUser.msn'
db.add_column('sso_ssouser', 'msn', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
# Adding field 'SSOUser.default_service_passwd'
db.add_column('sso_ssouser', 'default_service_passwd', self.gf('django.db.models.fields.CharField')(default='', max_length=200, blank=True), keep_default=False)
# Adding field 'SSOUser.icq'
db.add_column('sso_ssouser', 'icq', self.gf('django.db.models.fields.CharField')(default='', max_length=15, blank=True), keep_default=False)
# Deleting field 'SSOUser.api_service_password'
db.delete_column('sso_ssouser', 'api_service_password')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sso.service': {
'Meta': {'object_name': 'Service'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'api': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'settings_json': ('jsonfield.fields.JSONField', [], {'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'sso.serviceaccount': {
'Meta': {'object_name': 'ServiceAccount'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sso.Service']"}),
'service_uid': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'sso.ssouser': {
'Meta': {'object_name': 'SSOUser'},
'api_service_password': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
}
}
complete_apps = ['sso']

View File

@@ -0,0 +1,99 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'SSOUserNote'
db.create_table('sso_ssousernote', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='notes', to=orm['auth.User'])),
('note', self.gf('django.db.models.fields.TextField')()),
('created_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('date_created', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
))
db.send_create_signal('sso', ['SSOUserNote'])
def backwards(self, orm):
# Deleting model 'SSOUserNote'
db.delete_table('sso_ssousernote')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'sso.service': {
'Meta': {'object_name': 'Service'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'api': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'settings_json': ('jsonfield.fields.JSONField', [], {'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'sso.serviceaccount': {
'Meta': {'object_name': 'ServiceAccount'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sso.Service']"}),
'service_uid': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'sso.ssouser': {
'Meta': {'object_name': 'SSOUser'},
'api_service_password': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'sso.ssousernote': {
'Meta': {'object_name': 'SSOUserNote'},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notes'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['sso']

View File

@@ -0,0 +1,155 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ServiceAccount.character'
db.add_column('sso_serviceaccount', 'character', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['eve_api.EVEPlayerCharacter'], null=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'ServiceAccount.character'
db.delete_column('sso_serviceaccount', 'character_id')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'eve_api.eveplayeralliance': {
'Meta': {'object_name': 'EVEPlayerAlliance'},
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'date_founded': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'member_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ticker': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'})
},
'eve_api.eveplayercharacter': {
'Meta': {'object_name': 'EVEPlayerCharacter'},
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'attrib_charisma': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_intelligence': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_memory': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_perception': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_willpower': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'balance': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'corporation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCorporation']", 'null': 'True', 'blank': 'True'}),
'current_location_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'director': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'gender': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_logoff': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'race': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'roles': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['eve_api.EVEPlayerCharacterRole']", 'null': 'True', 'blank': 'True'}),
'total_sp': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
'eve_api.eveplayercharacterrole': {
'Meta': {'object_name': 'EVEPlayerCharacterRole'},
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'roleid': ('django.db.models.fields.CharField', [], {'max_length': '64'})
},
'eve_api.eveplayercorporation': {
'Meta': {'object_name': 'EVEPlayerCorporation'},
'alliance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerAlliance']", 'null': 'True', 'blank': 'True'}),
'alliance_join_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'applications': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'ceo_character': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCharacter']", 'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'logo_color1': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_color2': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_color3': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_graphic_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape1': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape2': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape3': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'member_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'shares': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'tax_rate': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'ticker': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'sso.service': {
'Meta': {'object_name': 'Service'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'api': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'settings_json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'sso.serviceaccount': {
'Meta': {'object_name': 'ServiceAccount'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'character': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCharacter']", 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sso.Service']"}),
'service_uid': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'sso.ssouser': {
'Meta': {'object_name': 'SSOUser'},
'api_service_password': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'sso.ssousernote': {
'Meta': {'object_name': 'SSOUserNote'},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notes'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['sso']

View File

@@ -0,0 +1,193 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'SSOUserIPAddress'
db.create_table('sso_ssouseripaddress', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('first_seen', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('last_seen', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
('ip_address', self.gf('django.db.models.fields.CharField')(max_length=200)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='ip_addresses', to=orm['auth.User'])),
))
db.send_create_signal('sso', ['SSOUserIPAddress'])
def backwards(self, orm):
# Deleting model 'SSOUserIPAddress'
db.delete_table('sso_ssouseripaddress')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'eve_api.eveplayeralliance': {
'Meta': {'ordering': "['date_founded']", 'object_name': 'EVEPlayerAlliance'},
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'date_founded': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'executor': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCorporation']", 'null': 'True', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'member_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'ticker': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'})
},
'eve_api.eveplayercharacter': {
'Meta': {'object_name': 'EVEPlayerCharacter'},
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'attrib_charisma': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_intelligence': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_memory': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_perception': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'attrib_willpower': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'balance': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'corporation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCorporation']", 'null': 'True', 'blank': 'True'}),
'corporation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'current_location_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'director': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'gender': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_logoff': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'race': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'roles': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['eve_api.EVEPlayerCharacterRole']", 'null': 'True', 'blank': 'True'}),
'security_status': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'skills': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['eve_api.EVESkill']", 'through': "orm['eve_api.EVEPlayerCharacterSkill']", 'symmetrical': 'False'}),
'total_sp': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
},
'eve_api.eveplayercharacterrole': {
'Meta': {'object_name': 'EVEPlayerCharacterRole'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'roleid': ('django.db.models.fields.CharField', [], {'max_length': '64'})
},
'eve_api.eveplayercharacterskill': {
'Meta': {'object_name': 'EVEPlayerCharacterSkill'},
'character': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCharacter']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'skill': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVESkill']"}),
'skillpoints': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'eve_api.eveplayercorporation': {
'Meta': {'object_name': 'EVEPlayerCorporation'},
'alliance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerAlliance']", 'null': 'True', 'blank': 'True'}),
'alliance_join_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
'api_last_updated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'applications': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'ceo_character': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCharacter']", 'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'logo_color1': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_color2': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_color3': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_graphic_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape1': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape2': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'logo_shape3': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'member_count': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'shares': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'tax_rate': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'ticker': ('django.db.models.fields.CharField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'eve_api.eveskill': {
'Meta': {'object_name': 'EVESkill'},
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVESkillGroup']", 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'eve_api.eveskillgroup': {
'Meta': {'object_name': 'EVESkillGroup'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'sso.service': {
'Meta': {'ordering': "['id']", 'object_name': 'Service'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'api': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'settings_json': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'})
},
'sso.serviceaccount': {
'Meta': {'ordering': "['user']", 'object_name': 'ServiceAccount'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'character': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['eve_api.EVEPlayerCharacter']", 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'service': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sso.Service']"}),
'service_uid': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'sso.ssouser': {
'Meta': {'object_name': 'SSOUser'},
'api_service_password': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'sso.ssouseripaddress': {
'Meta': {'ordering': "['user']", 'object_name': 'SSOUserIPAddress'},
'first_seen': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'last_seen': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ip_addresses'", 'to': "orm['auth.User']"})
},
'sso.ssousernote': {
'Meta': {'ordering': "['date_created']", 'object_name': 'SSOUserNote'},
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'notes'", 'to': "orm['auth.User']"})
}
}
complete_apps = ['sso']

View File

186
app/sso/models.py Normal file
View File

@@ -0,0 +1,186 @@
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, EVEPlayerCharacter
from services import get_api
## Exceptions
class CorporateOnlyService(Exception):
pass
class ExistingUser(Exception):
pass
class ServiceError(Exception):
pass
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)
def __unicode__(self):
return self.user.__unicode__()
@staticmethod
def create_user_profile(sender, instance, created, **kwargs):
if created:
profile, created = SSOUser.objects.get_or_create(user=instance)
signals.post_save.connect(SSOUser.create_user_profile, 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 SSOUserIPAddress(models.Model):
"""
Stores User Related IP Addresses
"""
first_seen = models.DateTimeField(auto_now_add=True, blank=False, null=False,
verbose_name="First sighting date/time",
help_text="Shows the first the user was seen at this IP.")
last_seen = models.DateTimeField(auto_now_add=True, blank=False, null=False,
verbose_name="First sighting date/time",
help_text="Shows the most recent time the user has been seen at this IP.")
ip_address = models.CharField("IP Address", max_length=200, blank=False)
user = models.ForeignKey(User, blank=False, null=False, related_name='ip_addresses')
def __unicode__(self):
return self.ip_address
class Meta:
verbose_name = 'User IP Addresse'
verbose_name_plural = 'User IP Addresses'
ordering = ['user']
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=True)
settings_json = JSONField("Service Settings", blank=True, default={})
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 __unicode__(self):
return self.name
def save(self):
if not self.settings_json or 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 = models.ForeignKey(EVEPlayerCharacter, null=True)
username = None
password = None
class Meta:
verbose_name = 'Service Account'
verbose_name_plural = 'Service Accounts'
ordering = ['user']
def __unicode__(self):
return u"%s: %s (%s)" % (self.service.name, self.user.username, self.service_uid)
def save(self):
if self.id:
org = ServiceAccount.objects.get(id=self.pk)
if org.active != self.active and self.service_uid:
if self.active:
self.service.api_class.enable_user(self.service_uid, '')
else:
self.service.api_class.disable_user(self.service_uid)
models.Model.save(self)
@staticmethod
def pre_delete_listener(**kwargs):
if not kwargs['instance'].service.api_class.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)

View File

@@ -0,0 +1,84 @@
import settings
from django.db import connections
def get_api(api):
if settings.DISABLE_SERVICES:
return BaseService()
try:
mod = __import__(api)
except ImportError:
raise Exception('Error creating service')
for i in api.split(".")[1:]:
mod = getattr(mod, i)
return getattr(mod, mod.ServiceClass)()
def list_apis():
import os.path, pkgutil
pkgpath = os.path.dirname(__file__)
return [name for _, name, _ in pkgutil.iter_modules([pkgpath])]
class BaseService():
"""
Base Service class, all service classes should inherit from this
"""
settings = { 'require_user': True,
'require_password': True,
'provide_login': False }
def add_user(self, username, password, **kwargs):
""" Add a user, returns a dict for that user """
return { 'username': username, 'password': password }
def check_user(self, username):
""" Check if the username exists """
return False
def check_uid(self, uid):
""" Check if a UID exists """
return self.check_user(uid)
def delete_user(self, uid):
""" Delete a user by uid """
return True
def disable_user(self, uid):
""" Disable a user by uid """
return True
def enable_user(self, uid, password):
""" Enable a user by uid """
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return True
def login(self, uid):
""" Login the user and provide cookies back """
pass
def update_groups(self, uid, groups, character=None):
"""" Update the UID's groups based on the provided list """
pass
class BaseDBService(BaseService):
@property
def db(self):
if not hasattr(self, '_db'):
self._db = connections[self.settings['database_name']]
return self._db
@property
def dbcursor(self):
if not hasattr(self, '_dbcursor'):
self._dbcursor = self.db.cursor()
return self._dbcursor

View File

@@ -0,0 +1,8 @@
from sso.services import BaseService
class DummyService(BaseService):
""" Always passes, good for a test service """
pass
ServiceClass = 'DummyService'

View File

@@ -0,0 +1,87 @@
import xmlrpclib
from sso.services import BaseService
import settings
from hashlib import md5
class IPBService(BaseService):
settings = { 'require_user': True,
'require_password': True,
'provide_login': False,
'use_auth_username': True,
'ipb_display_name': "[%(corp)s] - %(name)s",
'ipb_endpoint': 'http://testboard.aevumdecessus.com/interface/board/index.php',
'ipb_api_key': '51242ecca069b986106f04db1047cfc4',
'ipb_api_module': 'ipb' }
def exec_xmlrpc(self, func, **kwargs):
""" Send a XMLRPC request """
if not hasattr(self, '_server'):
self._server = xmlrpclib.Server(self.settings['ipb_endpoint'], verbose=True)
params = {}
for i in kwargs:
params[i] = kwargs[i]
params['api_key'] = self.settings['ipb_api_key']
params['api_module'] = self.settings['ipb_api_module']
return getattr(self._server, func)(params)
def _create_group(self, name):
""" Creates a IPB membergroup """
ret = self.exec_xmlrpc('createGroup', group_name=name)
if ret:
return {'name': name, 'id': ret['changeSuccessful']}
def add_user(self, username, password, **kwargs):
""" Add user to service """
password = md5(password).hexdigest()
details = { 'name': kwargs['character'].name,
'alli': kwargs['character'].corporation.alliance.ticker,
'corp': kwargs['character'].corporation.ticker }
display = self.settings['ipb_display_name'] % details
ret = self.exec_xmlrpc('createUser', username=username, email=kwargs['user'].email, display_name=display, password=password)
return username
def delete_user(self, uid):
""" Delete a user """
pass
def check_user(self, username):
""" Check if the username exists """
ret = self.exec_xmlrpc('fetchMember', search_type='email', search_string=username)
return ret
def disable_user(self, uid):
""" Disable a user """
pass
def enable_user(self, uid, password):
""" Enable a user """
pass
def reset_password(self, uid, password):
""" Reset the user's password """
pass
def update_groups(self, uid, groups, character=None):
user_id = self.check_user(uid)
# Get all IPB groups
# Iterate through the provided group list and create any missing ones
grplist = []
for g in groups:
if not g.name in grplist:
ret = self._create_group(g.name)
if ret:
grplist[ret['name']] = ret['id']
# Assign each group to the user id
for gk in grplist:
self.exec_xmlrpc('updateUserGroup', user_id=user_id, group_name=gk, action='ADD')
ServiceClass = 'IPBService'

View File

@@ -0,0 +1,151 @@
import xmlrpclib
from sso.services import BaseService
import settings
class JabberService(BaseService):
settings = { 'require_user': True,
'require_password': True,
'provide_login': False,
'use_auth_username': False,
'jabber_server': 'dredd.it',
'jabber_xmlrpc_url': 'http://127.0.0.1:4560',
'jabber_annouce_from': 'announcebot@pleaseignore.com'}
def exec_xmlrpc(self, func, **kwargs):
""" Send a XMLRPC request """
if not hasattr(self, '_server'):
self._server = xmlrpclib.Server(self.settings['jabber_xmlrpc_url'])
params = {}
for i in kwargs:
params[i] = kwargs[i]
return getattr(self._server, func)(params)
def add_user(self, username, password, **kwargs):
""" Add user to service """
username = username.lower()
res = self.exec_xmlrpc('register', user=username, host=self.settings['jabber_server'], password=password)
if res['res'] == 0:
if 'character' in kwargs:
self.exec_xmlrpc('set_nickname', user=username, host=self.settings['jabber_server'], nickname=kwargs['character'].name)
self.exec_xmlrpc('set_vcard2', user=username, host=self.settings['jabber_server'], name='ORG', subname='ORGNAME', content=kwargs['character'].corporation.name)
uid = "%s@%s" % (username.lower(), self.settings['jabber_server'])
if 'user' in kwargs:
self.update_groups(uid, kwargs['user'].groups.all())
return { 'username': uid, 'password': password }
else:
return False
def delete_user(self, uid):
""" Delete a user """
username, server = uid.lower().split("@")
for group in self.get_user_groups(uid):
self.exec_xmlrpc('srg_user_del', user=username, host=server, group=group, grouphost=server)
res = self.exec_xmlrpc('unregister', user=username, host=server)
if res['res'] == 0:
return True
else:
return False
def check_user(self, username):
""" Check if the username exists """
if '@' in username:
username, server = username.lower().split("@")
else:
server = self.settings['jabber_server']
res = self.exec_xmlrpc('check_account', user=username, host=server)
if res['res'] == 0:
return True
else:
return False
def disable_user(self, uid):
""" Disable a user """
username, server = uid.lower().split("@")
res = self.exec_xmlrpc('ban_account', host=server, user=username, reason='Auth account disable')
if res['res'] == 0:
return True
else:
return False
def enable_user(self, uid, password):
""" Enable a user """
self.reset_password(uid, password)
def reset_password(self, uid, password):
""" Reset the user's password """
username, server = uid.lower().split("@")
res = self.exec_xmlrpc('change_password', user=username, host=server, newpass=password)
if res['res'] == 0:
return True
else:
return False
def get_group_list(self, server):
srvgrp = self.exec_xmlrpc('srg_list', host=server)
return [grp['id'].lower() for grp in srvgrp['groups']]
def get_group_members(self, server, group):
members = self.exec_xmlrpc('srg_get_members', group=group, host=server)
return [x['member'].lower() for x in members['members']]
def get_user_groups(self, uid):
grouplist = []
username, server = uid.lower().split("@")
for grp in self.get_group_list(server):
if uid in self.get_group_members(server, grp):
grouplist.append(grp)
return grouplist
def update_groups(self, uid, groups, character=None):
username, server = uid.lower().split("@")
current_groups = self.get_user_groups(uid)
valid_groups = []
for group in groups:
groupname = group.name.lower().replace(' ', '-')
self.exec_xmlrpc('srg_create', group=groupname, host=server, name=group.name, description='', display='')
self.exec_xmlrpc('srg_user_add', user=username, host=server, group=groupname, grouphost=server)
valid_groups.append(groupname)
for group in (set(current_groups) - set(valid_groups)):
self.exec_xmlrpc('srg_user_del', user=username, host=server, group=groupname, grouphost=server)
def send_message(self, jid, msg):
# send_stanza_c2s user host resource stanza
username, server = jid.lower().split("@")
self.exec_xmlrpc('send_stanza_c2s', user=username, host=server, resource='auth', stanza=str(msg))
def announce(self, server, message, subject=None, all=False, users=[], groups=[]):
import xmpp
msg = xmpp.protocol.Message()
msg.setFrom(self.settings['jabber_announce_from'])
msg.setBody(message)
if subject:
msg.setSubject(subject)
if all:
msg.setTo('%s/announce/all-hosts/online' % server)
self.send_message(self.settings['jabber_announce_from'], msg)
return True
else:
if len(users):
for u in set(users):
msg.setTo(u)
self.send_message(self.settings['jabber_announce_from'], msg)
return True
elif len(groups):
tolist = []
for g in groups:
tolist.extend([x for x in self.get_group_members(server, g)])
return self.announce(server, message, subject, users=tolist)
else:
return False
ServiceClass = 'JabberService'

View File

@@ -0,0 +1,86 @@
import crypt
import random
import time
from django.db import transaction
from sso.services import BaseDBService
import settings
class MiningBuddyService(BaseDBService):
"""
Mining Buddy Class, allows registration and sign-in
"""
settings = { 'require_user': False,
'require_password': False,
'provide_login': False,
'use_auth_username': False,
'database_name': 'dreddit_mining',
'password_salt': 's98ss7fsc7fd2rf62ctcrlwztstnzve9toezexcsdhfgviuinusxcdtsvbrg' }
SQL_ADD_USER = r"INSERT INTO users (username, password, email, emailvalid, confirmed, rank) VALUES (%s, %s, %s, 1, 1, 2)"
SQL_ADD_API = r"INSERT INTO api_keys (userid, time, apiID, apiKey, api_valid, charid) values (%s, %s, %s, %s, 1, %s)"
SQL_DIS_USER = r"UPDATE users SET canLogin = 0 WHERE username = %s"
SQL_ENABLE_USER = r"UPDATE users SET canLogin = 1, password = %s WHERE username = %s"
SQL_CHECK_USER = r"SELECT username from users WHERE username = %s and deleted = 0"
SQL_DEL_USER = r"UPDATE users set deleted = 1 WHERE username = %s"
def _gen_salt(self):
return self.settings['password_salt']
def _gen_mb_hash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
return crypt.crypt(password, salt)
def _clean_username(self, username):
username = username.strip()
return username
def add_user(self, username, password, **kwargs):
""" Add a user """
pwhash = self._gen_mb_hash(password)
if 'user' in kwargs:
email = kwargs['user'].email
else:
email = ''
self.dbcursor.execute(self.SQL_ADD_USER, [self._clean_username(username), pwhash, email])
userid = self.dbcursor.lastrowid
api = kwargs['character'].eveaccount_set.all()[0]
self.dbcursor.execute(self.SQL_ADD_API, [userid, int(time.time()), api.api_user_id, api.api_key, kwargs['character'].id])
return { 'username': self._clean_username(username), 'password': password }
def check_user(self, username):
""" Check if the username exists """
self.dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self.dbcursor.fetchone()
if row:
return True
return False
def delete_user(self, uid):
""" Delete a user """
self.dbcursor.execute(self.SQL_DEL_USER, [uid])
return True
def disable_user(self, uid):
""" Disable a user """
self.dbcursor.execute(self.SQL_DIS_USER, [uid])
return True
def enable_user(self, uid, password):
""" Enable a user """
pwhash = self._gen_mb_hash(password)
self.dbcursor.execute(self.SQL_ENABLE_USER, [pwhash, uid])
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'MiningBuddyService'

View File

@@ -0,0 +1,610 @@
# -*- coding: utf-8 -*-
"""
* Copyright © 2009, withgod <withgod@sourceforge.net>
* 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
*
* Mumble-Django is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
"""
from time import time
from functools import wraps
from StringIO import StringIO
from os.path import exists, join
from os import unlink, name as os_name
from PIL import Image
from struct import pack, unpack
from zlib import compress, decompress, error
from mctl import MumbleCtlBase
import Ice, IcePy, tempfile
class ObjectInfo( object ):
""" Wraps arbitrary information to be easily accessed. """
def __init__( self, **kwargs ):
self.__dict__ = kwargs;
def __str__( self ):
return unicode( self );
def __repr__( self ):
return unicode( self );
def __unicode__( self ):
return unicode( self.__dict__ );
def __getitem__(self, key):
return self.__dict__[key]
def protectDjangoErrPage( func ):
""" Catch and reraise Ice exceptions to prevent the Django page from failing.
Since I need to "import Murmur", Django would try to read a murmur.py file
which doesn't exist, and thereby produce an IndexError exception. This method
erases the exception's traceback, preventing Django from trying to read any
non-existant files and borking.
"""
@wraps(func)
def protection_wrapper( self, *args, **kwargs ):
""" Call the original function and catch Ice exceptions. """
try:
return func( self, *args, **kwargs );
except Ice.Exception, err:
raise err;
protection_wrapper.innerfunc = func
return protection_wrapper;
@protectDjangoErrPage
def MumbleCtlIce( connstring, slicefile=None, icesecret=None ):
""" Choose the correct Ice handler to use (1.1.8 or 1.2.x), and make sure the
Murmur version matches the slice Version.
Optional parameters are the path to the slice file and the Ice secret
necessary to authenticate to Murmur.
The path can be omitted only if running Murmur 1.2.3 or later, which
exports a getSlice method to retrieve the Slice from.
"""
prop = Ice.createProperties([])
prop.setProperty("Ice.ImplicitContext", "Shared")
idd = Ice.InitializationData()
idd.properties = prop
ice = Ice.initialize(idd)
if icesecret:
ice.getImplicitContext().put( "secret", icesecret.encode("utf-8") )
prx = ice.stringToProxy( connstring.encode("utf-8") )
try:
prx.ice_ping()
except Ice.Exception:
raise EnvironmentError( "Murmur does not appear to be listening on this address (Ice ping failed)." )
try:
import Murmur
except ImportError:
# Try loading the Slice from Murmur directly via its getSlice method.
# See scripts/testdynamic.py in Mumble's Git repository.
try:
slice = IcePy.Operation( 'getSlice',
Ice.OperationMode.Idempotent, Ice.OperationMode.Idempotent,
True, (), (), (), IcePy._t_string, ()
).invoke(prx, ((), None))
except (TypeError, Ice.OperationNotExistException):
if not slicefile:
raise EnvironmentError(
"You didn't configure a slice file. Please set the SLICE variable in settings.py." )
if not exists( slicefile ):
raise EnvironmentError(
"The slice file does not exist: '%s' - please check the settings." % slicefile )
if " " in slicefile:
raise EnvironmentError(
"You have a space char in your Slice path. This will confuse Ice, please check." )
if not slicefile.endswith( ".ice" ):
raise EnvironmentError( "The slice file name MUST end with '.ice'." )
try:
Ice.loadSlice( slicefile )
except RuntimeError:
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
else:
if os_name == "nt":
# It weren't Windows if it didn't need to be treated differently. *sigh*
temppath = join( tempfile.gettempdir(), "Murmur.ice" )
slicetemp = open( temppath, "w+b" )
try:
slicetemp.write( slice )
finally:
slicetemp.close()
try:
Ice.loadSlice( temppath )
except RuntimeError:
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
finally:
unlink(temppath)
else:
slicetemp = tempfile.NamedTemporaryFile( suffix='.ice' )
try:
slicetemp.write( slice )
slicetemp.flush()
Ice.loadSlice( slicetemp.name )
except RuntimeError:
raise RuntimeError( "Slice preprocessing failed. Please check your server's error log." )
finally:
slicetemp.close()
import Murmur
meta = Murmur.MetaPrx.checkedCast(prx)
murmurversion = meta.getVersion()[:3]
if murmurversion == (1, 1, 8):
return MumbleCtlIce_118( connstring, meta );
elif murmurversion[:2] == (1, 2):
if murmurversion[2] < 2:
return MumbleCtlIce_120( connstring, meta );
elif murmurversion[2] == 2:
return MumbleCtlIce_122( connstring, meta );
elif murmurversion[2] == 3:
return MumbleCtlIce_123( connstring, meta );
raise NotImplementedError( "No ctl object available for Murmur version %d.%d.%d" % tuple(murmurversion) )
class MumbleCtlIce_118(MumbleCtlBase):
method = "ICE";
def __init__( self, connstring, meta ):
self.proxy = connstring;
self.meta = meta;
@protectDjangoErrPage
def _getIceServerObject(self, srvid):
return self.meta.getServer(srvid);
@protectDjangoErrPage
def getBootedServers(self):
ret = []
for x in self.meta.getBootedServers():
ret.append(x.id())
return ret
@protectDjangoErrPage
def getVersion( self ):
return self.meta.getVersion();
@protectDjangoErrPage
def getAllServers(self):
ret = []
for x in self.meta.getAllServers():
ret.append(x.id())
return ret
@protectDjangoErrPage
def getRegisteredPlayers(self, srvid, filter = ''):
users = self._getIceServerObject(srvid).getRegisteredPlayers( filter.encode( "UTF-8" ) )
ret = {};
for user in users:
ret[user.playerid] = ObjectInfo(
userid = int( user.playerid ),
name = unicode( user.name, "utf8" ),
email = unicode( user.email, "utf8" ),
pw = unicode( user.pw, "utf8" )
);
return ret
@protectDjangoErrPage
def getChannels(self, srvid):
return self._getIceServerObject(srvid).getChannels();
@protectDjangoErrPage
def getPlayers(self, srvid):
users = self._getIceServerObject(srvid).getPlayers()
ret = {};
for useridx in users:
user = users[useridx];
ret[ user.session ] = ObjectInfo(
session = user.session,
userid = user.playerid,
mute = user.mute,
deaf = user.deaf,
suppress = user.suppressed,
selfMute = user.selfMute,
selfDeaf = user.selfDeaf,
channel = user.channel,
name = user.name,
onlinesecs = user.onlinesecs,
bytespersec = user.bytespersec
);
return ret;
@protectDjangoErrPage
def getDefaultConf(self):
return self.setUnicodeFlag(self.meta.getDefaultConf())
@protectDjangoErrPage
def getAllConf(self, srvid):
conf = self.setUnicodeFlag(self._getIceServerObject(srvid).getAllConf())
info = {};
for key in conf:
if key == "playername":
info['username'] = conf[key];
else:
info[str(key)] = conf[key];
return info;
@protectDjangoErrPage
def newServer(self):
return self.meta.newServer().id()
@protectDjangoErrPage
def isBooted( self, srvid ):
return bool( self._getIceServerObject(srvid).isRunning() );
@protectDjangoErrPage
def start( self, srvid ):
self._getIceServerObject(srvid).start();
@protectDjangoErrPage
def stop( self, srvid ):
self._getIceServerObject(srvid).stop();
@protectDjangoErrPage
def deleteServer( self, srvid ):
if self._getIceServerObject(srvid).isRunning():
self._getIceServerObject(srvid).stop()
self._getIceServerObject(srvid).delete()
@protectDjangoErrPage
def setSuperUserPassword(self, srvid, value):
self._getIceServerObject(srvid).setSuperuserPassword( value.encode( "UTF-8" ) )
@protectDjangoErrPage
def getConf(self, srvid, key):
if key == "username":
key = "playername";
return self._getIceServerObject(srvid).getConf( key )
@protectDjangoErrPage
def setConf(self, srvid, key, value):
if key == "username":
key = "playername";
if value is None:
value = ''
self._getIceServerObject(srvid).setConf( key, value.encode( "UTF-8" ) )
@protectDjangoErrPage
def registerPlayer(self, srvid, name, email, password):
mumbleid = self._getIceServerObject(srvid).registerPlayer( name.encode( "UTF-8" ) )
self.setRegistration( srvid, mumbleid, name, email, password );
return mumbleid;
@protectDjangoErrPage
def unregisterPlayer(self, srvid, mumbleid):
self._getIceServerObject(srvid).unregisterPlayer(mumbleid)
@protectDjangoErrPage
def getRegistration(self, srvid, mumbleid):
user = self._getIceServerObject(srvid).getRegistration(mumbleid)
return ObjectInfo(
userid = mumbleid,
name = user.name,
email = user.email,
pw = '',
);
@protectDjangoErrPage
def setRegistration(self, srvid, mumbleid, name, email, password):
import Murmur
user = Murmur.Player()
user.playerid = mumbleid;
user.name = name.encode( "UTF-8" )
user.email = email.encode( "UTF-8" )
user.pw = password.encode( "UTF-8" )
# update*r*egistration r is lowercase...
return self._getIceServerObject(srvid).updateregistration(user)
@protectDjangoErrPage
def getACL(self, srvid, channelid):
# need to convert acls to say "userid" instead of "playerid". meh.
raw_acls, raw_groups, raw_inherit = self._getIceServerObject(srvid).getACL(channelid)
acls = [ ObjectInfo(
applyHere = rule.applyHere,
applySubs = rule.applySubs,
inherited = rule.inherited,
userid = rule.playerid,
group = rule.group,
allow = rule.allow,
deny = rule.deny,
)
for rule in raw_acls
];
return acls, raw_groups, raw_inherit;
@protectDjangoErrPage
def setACL(self, srvid, channelid, acls, groups, inherit):
import Murmur
ice_acls = [];
for rule in acls:
ice_rule = Murmur.ACL();
ice_rule.applyHere = rule.applyHere;
ice_rule.applySubs = rule.applySubs;
ice_rule.inherited = rule.inherited;
ice_rule.playerid = rule.userid;
ice_rule.group = rule.group;
ice_rule.allow = rule.allow;
ice_rule.deny = rule.deny;
ice_acls.append(ice_rule);
return self._getIceServerObject(srvid).setACL( channelid, ice_acls, groups, inherit );
@protectDjangoErrPage
def getTexture(self, srvid, mumbleid):
texture = self._getIceServerObject(srvid).getTexture(mumbleid)
if len(texture) == 0:
raise ValueError( "No Texture has been set." );
# this returns a list of bytes.
try:
decompressed = decompress( texture );
except error, err:
raise ValueError( err )
# iterate over 4 byte chunks of the string
imgdata = "";
for idx in range( 0, len(decompressed), 4 ):
# read 4 bytes = BGRA and convert to RGBA
# manual wrote getTexture returns "Textures are stored as zlib compress()ed 600x60 32-bit RGBA data."
# http://mumble.sourceforge.net/slice/Murmur/Server.html#getTexture
# but return values BGRA X(
bgra = unpack( "4B", decompressed[idx:idx+4] );
imgdata += pack( "4B", bgra[2], bgra[1], bgra[0], bgra[3] );
# return an 600x60 RGBA image object created from the data
return Image.fromstring( "RGBA", ( 600, 60 ), imgdata );
@protectDjangoErrPage
def setTexture(self, srvid, mumbleid, infile):
# open image, convert to RGBA, and resize to 600x60
img = infile.convert( "RGBA" ).transform( ( 600, 60 ), Image.EXTENT, ( 0, 0, 600, 60 ) );
# iterate over the list and pack everything into a string
bgrastring = "";
for ent in list( img.getdata() ):
# ent is in RGBA format, but Murmur wants BGRA (ARGB inverse), so stuff needs
# to be reordered when passed to pack()
bgrastring += pack( "4B", ent[2], ent[1], ent[0], ent[3] );
# compress using zlib
compressed = compress( bgrastring );
# pack the original length in 4 byte big endian, and concat the compressed
# data to it to emulate qCompress().
texture = pack( ">L", len(bgrastring) ) + compressed;
# finally call murmur and set the texture
self._getIceServerObject(srvid).setTexture(mumbleid, texture)
@protectDjangoErrPage
def verifyPassword(self, srvid, username, password):
return self._getIceServerObject(srvid).verifyPassword(username, password);
@staticmethod
def setUnicodeFlag(data):
ret = ''
if isinstance(data, tuple) or isinstance(data, list) or isinstance(data, dict):
ret = {}
for key in data.keys():
ret[MumbleCtlIce_118.setUnicodeFlag(key)] = MumbleCtlIce_118.setUnicodeFlag(data[key])
else:
ret = unicode(data, 'utf-8')
return ret
class MumbleCtlIce_120(MumbleCtlIce_118):
@protectDjangoErrPage
def getRegisteredPlayers(self, srvid, filter = ''):
users = self._getIceServerObject( srvid ).getRegisteredUsers( filter.encode( "UTF-8" ) )
ret = {};
for id in users:
ret[id] = ObjectInfo(
userid = id,
name = unicode( users[id], "utf8" ),
email = '',
pw = ''
);
return ret
@protectDjangoErrPage
def getPlayers(self, srvid):
userdata = self._getIceServerObject(srvid).getUsers();
for key in userdata:
if isinstance( userdata[key], str ):
userdata[key] = userdata[key].decode( "UTF-8" )
return userdata
@protectDjangoErrPage
def getState(self, srvid, sessionid):
userdata = self._getIceServerObject(srvid).getState(sessionid);
for key in userdata.__dict__:
attr = getattr( userdata, key )
if isinstance( attr, str ):
setattr( userdata, key, attr.decode( "UTF-8" ) )
return userdata
@protectDjangoErrPage
def registerPlayer(self, srvid, name, email, password):
# To get the real values of these ENUM entries, try
# Murmur.UserInfo.UserX.value
import Murmur
user = {
Murmur.UserInfo.UserName: name.encode( "UTF-8" ),
Murmur.UserInfo.UserEmail: email.encode( "UTF-8" ),
Murmur.UserInfo.UserPassword: password.encode( "UTF-8" ),
};
return self._getIceServerObject(srvid).registerUser( user );
@protectDjangoErrPage
def unregisterPlayer(self, srvid, mumbleid):
self._getIceServerObject(srvid).unregisterUser(mumbleid)
@protectDjangoErrPage
def getRegistration(self, srvid, mumbleid):
reg = self._getIceServerObject( srvid ).getRegistration( mumbleid )
user = ObjectInfo( userid=mumbleid, name="", email="", comment="", hash="", pw="" );
import Murmur
if Murmur.UserInfo.UserName in reg: user.name = reg[Murmur.UserInfo.UserName];
if Murmur.UserInfo.UserEmail in reg: user.email = reg[Murmur.UserInfo.UserEmail];
if Murmur.UserInfo.UserComment in reg: user.comment = reg[Murmur.UserInfo.UserComment];
if Murmur.UserInfo.UserHash in reg: user.hash = reg[Murmur.UserInfo.UserHash];
return user;
@protectDjangoErrPage
def setRegistration(self, srvid, mumbleid, name, email, password):
import Murmur
user = {
Murmur.UserInfo.UserName: name.encode( "UTF-8" ),
Murmur.UserInfo.UserEmail: email.encode( "UTF-8" ),
Murmur.UserInfo.UserPassword: password.encode( "UTF-8" ),
};
return self._getIceServerObject( srvid ).updateRegistration( mumbleid, user )
@protectDjangoErrPage
def getAllConf(self, srvid):
conf = self.setUnicodeFlag(self._getIceServerObject(srvid).getAllConf())
info = {};
for key in conf:
if key == "playername" and conf[key]:
# Buggy database transition from 1.1.8 -> 1.2.0
# Store username as "username" field and set playername field to empty
info['username'] = conf[key];
self.setConf( srvid, "playername", "" );
self.setConf( srvid, "username", conf[key] );
else:
info[str(key)] = conf[key];
return info;
@protectDjangoErrPage
def getConf(self, srvid, key):
return self._getIceServerObject(srvid).getConf( key )
@protectDjangoErrPage
def setConf(self, srvid, key, value):
if value is None:
value = ''
self._getIceServerObject(srvid).setConf( key, value.encode( "UTF-8" ) )
@protectDjangoErrPage
def getACL(self, srvid, channelid):
return self._getIceServerObject(srvid).getACL(channelid)
@protectDjangoErrPage
def setACL(self, srvid, channelid, acls, groups, inherit):
return self._getIceServerObject(srvid).setACL( channelid, acls, groups, inherit );
@protectDjangoErrPage
def getBans(self, srvid):
return self._getIceServerObject(srvid).getBans();
@protectDjangoErrPage
def setBans(self, srvid, bans):
return self._getIceServerObject(srvid).setBans(bans);
@protectDjangoErrPage
def addBanForSession(self, srvid, sessionid, **kwargs):
session = self.getState(srvid, sessionid);
if "bits" not in kwargs:
kwargs["bits"] = 128;
if "start" not in kwargs:
kwargs["start"] = int(time());
if "duration" not in kwargs:
kwargs["duration"] = 3600;
return self.addBan(srvid, address=session.address, **kwargs);
@protectDjangoErrPage
def addBan(self, srvid, **kwargs):
for key in kwargs:
if isinstance( kwargs[key], unicode ):
kwargs[key] = kwargs[key].encode("UTF-8")
from Murmur import Ban
srvbans = self.getBans(srvid);
srvbans.append( Ban( **kwargs ) );
return self.setBans(srvid, srvbans);
@protectDjangoErrPage
def kickUser(self, srvid, userid, reason=""):
return self._getIceServerObject(srvid).kickUser( userid, reason.encode("UTF-8") );
class MumbleCtlIce_122(MumbleCtlIce_120):
@protectDjangoErrPage
def getTexture(self, srvid, mumbleid):
raise ValueError( "This method is buggy in 1.2.2, sorry dude." );
@protectDjangoErrPage
def setTexture(self, srvid, mumbleid, infile):
buf = StringIO()
infile.save( buf, "PNG" )
buf.seek(0)
self._getIceServerObject(srvid).setTexture(mumbleid, buf.read())
class MumbleCtlIce_123(MumbleCtlIce_120):
@protectDjangoErrPage
def getRawTexture(self, srvid, mumbleid):
return self._getIceServerObject(srvid).getTexture(mumbleid)
@protectDjangoErrPage
def getTexture(self, srvid, mumbleid):
texture = self.getRawTexture(srvid, mumbleid)
if len(texture) == 0:
raise ValueError( "No Texture has been set." );
from StringIO import StringIO
try:
return Image.open( StringIO( texture ) )
except IOError, err:
raise ValueError( err )
@protectDjangoErrPage
def setTexture(self, srvid, mumbleid, infile):
buf = StringIO()
infile.save( buf, "PNG" )
buf.seek(0)
self._getIceServerObject(srvid).setTexture(mumbleid, buf.read())

View File

@@ -0,0 +1,164 @@
from sso.services import BaseService
from MumbleCtlIce import MumbleCtlIce
import Ice
class MumbleService(BaseService):
settings = { 'require_user': True,
'require_password': True,
'provide_login': False,
'use_auth_username': False,
'mumble_server_id': 2,
'name_format': r'%(alliance)s - %(corporation)s - %(name)s',
'connection_string': 'Meta:tcp -h 127.0.0.1 -p 6502',
'ice_file': 'Murmur.ice' }
def __init__(self):
try:
Ice.loadSlice(self.settings['ice_file'])
import Murmur
self.mur = Murmur
except:
pass
@property
def mumblectl(self):
if not hasattr(self, '_mumblectl'):
try:
self._mumblectl = MumbleCtlIce(self.settings['connection_string'], self.settings['ice_file'])
except:
self._mumblectl = None
return self._mumblectl
def add_user(self, username, password, **kwargs):
""" Add a user, returns a UID for that user """
details = { 'name': username,
'alliance': kwargs['character'].corporation.alliance.ticker,
'corporation': kwargs['character'].corporation.ticker }
username = self.settings['name_format'] % details
if self.raw_add_user(username, kwargs['user'].email, password):
self.update_groups(username, kwargs['user'].groups.all())
return { 'username': username, 'password': password }
else:
return False
def raw_add_user(self, username, email, password):
if self.mumblectl and self.mumblectl.registerPlayer(self.settings['mumble_server_id'], username, email, password):
return username
return False
def check_user(self, username):
""" Check if the username exists """
if self.mumblectl and len(self.mumblectl.getRegisteredPlayers(self.settings['mumble_server_id'], username)):
return True
return False
def delete_user(self, uid):
""" Delete a user by uid """
if self.mumblectl:
ids = self.mumblectl.getRegisteredPlayers(self.settings['mumble_server_id'], uid)
if len(ids) > 0:
for accid in ids:
acc = ids[accid]
self.mumblectl.unregisterPlayer(self.settings['mumble_server_id'], acc['userid'])
return True
def disable_user(self, uid):
""" Disable a user by uid """
self.delete_user(uid)
return True
def enable_user(self, uid, password):
""" Enable a user by uid """
if self.mumblectl:
ids = self.mumblectl.getRegisteredPlayers(self.settings['mumble_server_id'], uid)
if len(ids) > 0:
for accid in ids:
acc = ids[accid]
self.mumblectl.setRegistration(self.settings['mumble_server_id'], acc['userid'], acc['name'], acc['email'], password)
return True
else:
if self.raw_add_user(uid, '', password):
return True
return False
def set_user(self, uid, name = ''):
""" Set values ona user by uid """
if self.mumblectl:
ids = self.mumblectl.getRegisteredPlayers(self.settings['mumble_server_id'], uid)
if len(ids) > 0:
for accid in ids:
acc = ids[accid]
self.mumblectl.setRegistration(self.settings['mumble_server_id'], acc['userid'], name, acc['email'], acc['pw'])
return True
return False
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
def login(uid):
""" Login the user and provide cookies back """
pass
def _create_groups(self, groups):
""" Processes a list of groups and makes sure that related mumble groups exist """
acls = self.mumblectl.getACL(self.settings['mumble_server_id'], 0)
glist = []
for mgroup in acls[1]:
glist.append(mgroup.name)
newgroups = False
for agroup in groups:
if not str(agroup.name.replace(' ', '').lower()) in glist:
group = self.mur.Group()
group.name = str(agroup.name.replace(' ', '').lower())
group.members = []
group.add = []
group.remove = []
group.inheritable = True
group.inherit = True
group.inherited = False
acls[1].append(group)
newgroups = True
if newgroups:
self.mumblectl.setACL(self.settings['mumble_server_id'], 0, acls[0], acls[1], acls[2])
return acls
def update_groups(self, uid, groups, character=None):
""" Update the UID's groups based on the provided list """
user = self.mumblectl.getRegisteredPlayers(self.settings['mumble_server_id'], uid)
if not user:
return False
user = user.values()[0]
acls = self._create_groups(groups)
#acls = self.mumblectl.getACL(self.settings['mumble_server_id'], 0)
for agroup in groups:
gid = 0
for mgroup in acls[1]:
if mgroup.name == agroup.name.replace(' ', '').lower():
if not user['userid'] in acls[1][gid].members:
acls[1][gid].add.append(user['userid'])
acls[1][gid].members.append(user['userid'])
else:
if user['userid'] in acls[1][gid].members:
acls[1][gid].remove.append(user['userid'])
acls[1][gid].remove.remove(user['userid'])
gid = gid + 1
self.mumblectl.setACL(self.settings['mumble_server_id'], 0, acls[0], acls[1], acls[2])
return True
ServiceClass = 'MumbleService'

View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
"""
* Copyright © 2009, withgod <withgod@sourceforge.net>
* 2009-2010, Michael "Svedrin" Ziegler <diese-addy@funzt-halt.net>
*
* Mumble-Django is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
"""
import re
class MumbleCtlBase(object):
""" This class defines the base interface that the Mumble model expects. """
cache = {};
@staticmethod
def newInstance( connstring, slicefile=None, icesecret=None ):
""" Create a new CTL object for the given connstring.
Optional parameters are the path to the slice file and the
Ice secret necessary to authenticate to Murmur.
The path can be omitted only if using DBus or running Murmur
1.2.3 or later, which exports a getSlice method to retrieve
the Slice from.
"""
# check cache
if connstring in MumbleCtlBase.cache:
return MumbleCtlBase.cache[connstring];
# connstring defines whether to connect via ICE or DBus.
# Dbus service names: some.words.divided.by.periods
# ICE specs are WAY more complex, so if DBus doesn't match, use ICE.
rd = re.compile( r'^(\w+\.)*\w+$' );
if rd.match( connstring ):
from MumbleCtlDbus import MumbleCtlDbus
ctl = MumbleCtlDbus( connstring )
else:
from MumbleCtlIce import MumbleCtlIce
ctl = MumbleCtlIce( connstring, slicefile, icesecret )
MumbleCtlBase.cache[connstring] = ctl;
return ctl;
@staticmethod
def clearCache():
MumbleCtlBase.cache = {};

View File

@@ -0,0 +1,98 @@
import hashlib
import random
from django.db import load_backend, transaction, IntegrityError
from sso.services import BaseDBService
import settings
class PhpBBService(BaseDBService):
"""
PHPBB Class, allows registration and sign-in
"""
settings = { 'require_user': False,
'require_password': False,
'provide_login': False,
'use_auth_username': False }
SQL_ADD_USER = r"INSERT INTO phpbb_users (username, username_clean, user_password, user_email) VALUES (%s, %s, %s, %s)"
SQL_DIS_USER = r"DELETE FROM phpbb_user_groups where user_id = (SELECT user_id FROM phpbb_user WHERE username = %s)"
SQL_CHECK_USER = r"SELECT username from phpbb_user WHERE username = %s"
SQL_ADD_USER_GROUP = r"INSERT INTO phpbb_user_group (group_id, user_id) VALUES (%s, %s)"
SQL_GET_GROUP = r"SELECT group_id from phpbb_groups WHERE group_name = %s"
SQL_ADD_GROUP = r"INSERT INTO phpbb_groups (group_name) VALUES (%s)"
def _gen_salt(self):
return "%x" % random.randint(0, 2147483647)
def _gen_hash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
hash = hashlib.md5('%s-%s' % (salt, hashlib.md5(password).hexdigest())).hexdigest()
return ":B:%s:%s" % (salt, hash)
def add_user(self, username, password, **kwargs):
""" Add a user """
if 'user' in kwargs:
email = kwargs['user'].email
groups = kwargs['user'].groups.all()
else:
email = ''
pwhash = self._gen_hash(password)
self._dbcursor.execute(self.SQL_ADD_USER, [username, pwhash, email])
self.update_groups(username)
return { 'username': username, 'password': password }
def update_groups(self, username, groups, character=None):
self._dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self._dbcursor.fetchone()
user_id = row['user_id']
for group in groups:
self._dbcursor.execute(self.SQL_GET_GROUP, [group.name])
row = self._dbcursor.fetchone()
if not row:
self._dbcursor.execute(self.SQL_ADD_GROUP, [group.name])
self._dbcursor.execute(self.SQL_GET_GROUP, [group.name])
row = self._dbcursor.fetchone()
self._dbcursor.execute(self.SQL_ADD_USER_GROUP, [row['group_id'], user_id])
def check_user(self, username):
""" Check if the username exists """
self._dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self._dbcursor.fetchone()
if row:
return True
return False
def delete_user(self, uid):
""" Delete a user """
return True
def disable_user(self, uid):
""" Disable a user """
#self._dbcursor.execute(self.SQL_DIS_USER, [self._gen_user_token(), uid])
try:
self._dbcursor.execute(self.SQL_DIS_GROUP, [uid])
except IntegrityError:
# Record already exists, skip it
pass
return True
def enable_user(self, uid, password):
""" Enable a user """
pwhash = self._gen_mw_hash(password)
self._dbcursor.execute(self.SQL_ENABLE_USER, [pwhash, uid])
self._dbcursor.execute(self.SQL_ENABLE_GROUP, [uid])
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'PhpBBService'

View File

@@ -0,0 +1,77 @@
import hashlib
import random
from django.db import transaction, IntegrityError
from sso.services import BaseDBService
import settings
class POSTrackerService(BaseDBService):
"""
POS Tracker Class, allows registration.
"""
settings = { 'require_user': False,
'require_password': False,
'provide_login': False,
'use_auth_username': False,
'database_name': 'dreddit_pos' }
SQL_ADD_USER = r"INSERT INTO pos3_user (eve_id, name, pass, email, access, corp, alliance_id) VALUES (%s, %s, %s, %s, 1, %s, %s)"
SQL_DIS_USER = r"UPDATE pos3_user SET pass = '', away = 1 WHERE name = %s"
SQL_DEL_USER = r"DELETE FROM pos3_user WHERE name = %s"
SQL_ENABLE_USER = r"UPDATE pos3_user SET pass = %s, away = 0 WHERE name = %s"
SQL_CHECK_USER = r"SELECT name from pos3_user WHERE name = %s"
def _gen_salt(self):
return hashlib.md5("%x" % random.randint(0, 2147483647)).hexdigest()[:8]
def _gen_pwhash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
pwhash = hashlib.md5("%s%s" % (salt, password)).hexdigest()
return (pwhash, salt)
def add_user(self, username, password, **kwargs):
""" Add a user """
email = kwargs['user'].email
pwhash, salt = self._gen_pwhash(password)
eveid = kwargs['character'].eveaccount_set.all()[0].api_user_id
corpname = kwargs['character'].corporation.name
if kwargs['character'].corporation and kwargs['character'].corporation.alliance:
allianceid = kwargs['character'].corporation.alliance.id
else:
allianceid = 0
self.dbcursor.execute(self.SQL_ADD_USER, [eveid, username, "%s%s" % (salt, pwhash) , email, corpname, allianceid])
return { 'username': username, 'password': password }
def check_user(self, username):
""" Check if the username exists """
self.dbcursor.execute(self.SQL_CHECK_USER, [username])
row = self.dbcursor.fetchone()
if row:
return True
return False
def delete_user(self, uid):
""" Delete a user """
self.dbcursor.execute(self.SQL_DEL_USER, [uid])
return True
def disable_user(self, uid):
""" Disable a user """
self.dbcursor.execute(self.SQL_DIS_USER, [uid])
return True
def enable_user(self, uid, password):
""" Enable a user """
pwhash, salt = self._gen_pwhash(password)
self.dbcursor.execute(self.SQL_ENABLE_USER, ["%s%s" % (salt, pwhash), uid])
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'POSTrackerService'

View File

@@ -0,0 +1,70 @@
import hashlib
import random
from django.db import load_backend, transaction, IntegrityError
from sso.services import BaseDBService
import settings
class QMSService(BaseDBService):
"""
QMS Class, allows registration and sign-in
"""
settings = { 'require_user': False,
'require_password': False,
'provide_login': False,
'use_auth_username': False,
'database_name': 'dreddit_qms' }
SQL_ADD_USER = r"INSERT INTO users (ssoid, Name, passhash, salt, Email, certificate) VALUES (%s, %s, %s, %s, %s, %s)"
SQL_DIS_USER = r"UPDATE users SET passhash = '' WHERE ssoid = %s"
SQL_ENABLE_USER = r"UPDATE users SET passhash = %s, salt = %s, certificate = %s WHERE ssoid = %s"
SQL_CHECK_USER = r"SELECT ssoid from users WHERE ssoid = %s"
def _gen_salt(self):
return hashlib.md5("%x" % random.randint(0, 2147483647)).hexdigest()[:6]
def _gen_pwhash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
pwhash = hashlib.md5(password).hexdigest()
cert = hashlib.md5('%s%s' % (pwhash, salt)).hexdigest()
return (pwhash, salt, cert)
def add_user(self, username, password, **kwargs):
""" Add a user """
email = kwargs['user'].email
pwhash, salt, cert = self._gen_pwhash(password)
self.dbcursor.execute(self.SQL_ADD_USER, [username, username, pwhash, salt, email, cert])
return { 'username': username, 'password': password }
def check_user(self, username):
""" Check if the username exists """
self.dbcursor.execute(self.SQL_CHECK_USER, [username])
row = self.dbcursor.fetchone()
if row:
return True
return False
def delete_user(self, uid):
""" Delete a user """
#self.dbcursor.execute(self.SQL_DEL_REV, [uid])
#self.dbcursor.execute(self.SQL_DEL_USER, [uid])
return True
def disable_user(self, uid):
""" Disable a user """
self.dbcursor.execute(self.SQL_DIS_USER, [uid])
return True
def enable_user(self, uid, password):
""" Enable a user """
pwhash, salt, cert = self._gen_pwhash(password)
self.dbcursor.execute(self.SQL_ENABLE_USER, [pwhash, salt, cert, uid])
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'QMSService'

View File

@@ -0,0 +1,78 @@
import hashlib
import random
from sso.services import BaseDBService
import settings
class SMFService(BaseDBService):
"""
SMF Class, allows registration and sign-in
"""
settings = { 'require_user': True,
'require_password': True,
'provide_login': False,
'use_auth_username': False
'can_delete': False }
SQL_ADD_USER = r"INSERT INTO phpbb_users (username, username_clean, user_password, user_email) VALUES (%s, %s, %s, %s)"
SQL_DIS_USER = r"DELETE FROM phpbb_user_groups where user_id = (SELECT user_id FROM phpbb_user WHERE username = %s)"
SQL_CHECK_USER = r"SELECT username from phpbb_user WHERE username = %s"
SQL_ADD_USER_GROUP = r"INSERT INTO phpbb_user_group (group_id, user_id) VALUES (%s, %s)"
SQL_GET_GROUP = r"SELECT group_id from phpbb_groups WHERE group_name = %s"
SQL_ADD_GROUP = r"INSERT INTO phpbb_groups (group_name) VALUES (%s)"
def _gen_salt(self):
return "%x" % random.randint(0, 2147483647)
def _gen_hash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
hash = hashlib.md5('%s-%s' % (salt, hashlib.md5(password).hexdigest())).hexdigest()
return ":B:%s:%s" % (salt, hash)
def add_user(self, username, password, **kwargs):
""" Add a user """
if 'user' in kwargs:
email = kwargs['user'].email
else:
email = ''
pwhash = self._gen_hash(password)
self._dbcursor.execute(self.SQL_ADD_USER, [username, pwhash, email])
self.update_groups(username)
return { 'username': username, 'password': password }
def update_groups(self, username, groups, character=None):
self._dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self._dbcursor.fetchone()
user_id = row['id_member']
smfgrps = self._get_smf_groups()
grpids = []
for group in groups:
if group.name in smfgrps:
grpids.append(smfgrps[group.name])
else:
grpids.append(self._create_smf_group(group.name))
# Update DB with grouplist
self._dbcursor.execute(self.SQL_UPDATE_GROUPS, [user_id, ','.join(groupids)])
def check_user(self, username):
""" Check if the username exists """
self._dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self._dbcursor.fetchone()
if row:
return True
return False
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'SMFService'

View File

@@ -0,0 +1,195 @@
import urllib
from sso.services import BaseService
from ts3 import TS3Server
class TS3Service(BaseService):
settings = { 'require_user': True,
'require_password': False,
'provide_login': False,
'use_auth_username': False,
'host': 'mumble.pleaseignore.com',
'port': 10011,
'sa_login': 'serveradmin',
'sa_password': '',
'vhost_id': 0,
'authed_sgid': 12,
'name_format': '%(alliance)s - %(corporation)s - %(name)s',
'bookmark_name': 'TEST Alliance TS3',
'ignore_groups': [6]}
def __init__(self):
pass
@property
def conn(self):
if not hasattr(self, '_conn'):
self._conn = TS3Server(self.settings['host'], self.settings['port'])
self._conn.login(self.settings['sa_login'], self.settings['sa_password'])
self._conn.use(self.settings['vhost_id'])
return self._conn
def add_user(self, username, password, **kwargs):
""" Add a user, returns a UID for that user """
details = { 'name': username,
'corporation': kwargs['character'].corporation.ticker }
if kwargs['character'].corporation.alliance:
details['alliance'] = kwargs['character'].corporation.alliance.ticker
else:
details['alliance'] = None
username = self.settings['name_format'] % details
ret = self.conn.send_command('tokenadd', {'tokentype': 0, 'tokenid1': self.settings['authed_sgid'], 'tokenid2': 0, 'tokendescription': kwargs['character'].name.replace(' ', ''), 'tokencustomset': "ident=sso_uid value=%s|ident=sso_userid value=%s|ident=eve_charid value=%s" % (kwargs['character'].name.replace(' ', ''), kwargs['user'].id, kwargs['character'].id) })
if 'keys' in ret and 'token' in ret['keys']:
token = ret['keys']['token']
url = "<a href='ts3server://%s?token=%s'>Click this link to connect and register on TS3</a>" % (self.settings['host'], token)
return { 'username': kwargs['character'].name.replace(' ', ''), 'display name': username, 'permission token': token, 'registration url': url }
return None
def check_user(self, uid):
""" Check if the username exists """
# Lookup user using customsearch with sso_uid=uid
return self._get_userid(uid)
def delete_user(self, uid):
""" Delete a user by uid """
user = self._get_userid(uid)
if user:
for client in self.conn.send_command('clientlist'):
if client['keys']['client_database_id'] == user:
self.conn.send_command('clientkick', {'clid': client['keys']['clid'], 'reasonid': 5, 'reasonmsg': 'Auth service deleted'})
ret = self.conn.send_command('clientdbdelete', {'cldbid': user })
if ret == '0':
return True
else:
return True
def disable_user(self, uid):
""" Disable a user by uid """
user = self._get_userid(uid)
if user:
ret = self.conn.send_command('servergroupdelclient', {'sgid': self.settings['authed_sgid'], 'cldbid': user })
if ret == '0':
groups = self._user_group_list(user)
for group in groups:
self.conn.send_command('servergroupdelclient', {'sgid': groups[group], 'cldbid': user })
return True
return False
def enable_user(self, uid, password):
""" Enable a user by uid """
user = self._get_userid(uid)
if user:
ret = self.conn.send_command('servergroupaddclient', {'sgid': self.settings['authed_sgid'], 'cldbid': user })
if ret == '0':
return True
def reset_password(self, uid, password):
""" Reset the user's password """
pass
def login(uid):
""" Login the user and provide cookies back """
pass
def _group_by_name(self, groupname):
if not hasattr(self, '__group_cache') or not self.__group_cache:
self.__group_cache = self.conn.send_command('servergrouplist')
for group in self.__group_cache:
if group['keys']['name'] == groupname:
return group['keys']['sgid']
return None
def _get_userid(self, uid):
""" Finds the TS3 ID of a user from their UID """
ret = self.conn.send_command('customsearch', {'ident': 'sso_uid', 'pattern': uid})
if ret and 'keys' in ret and 'cldbid' in ret['keys']:
return ret['keys']['cldbid']
def _create_group(self, groupname):
""" Creates a Server Group and returns the SGID """
sgid = self._group_by_name(groupname)
if not sgid:
ret = self.conn.send_command('servergroupadd', { 'name': groupname })
self.__group_cache = None
sgid = ret['keys']['sgid']
self.conn.send_command('servergroupaddperm', { 'sgid': sgid, 'permsid': 'i_group_needed_modify_power', 'permvalue': 75, 'permnegated': 0, 'permskip': 0 })
self.conn.send_command('servergroupaddperm', { 'sgid': sgid, 'permsid': 'i_group_needed_member_add_power', 'permvalue': 100, 'permnegated': 0, 'permskip': 0 })
self.conn.send_command('servergroupaddperm', { 'sgid': sgid, 'permsid': 'i_group_needed_member_remove_power', 'permvalue': 100, 'permnegated': 0, 'permskip': 0 })
return sgid
def _group_list(self):
""" List of all groups on the TS server """
if not hasattr(self, '__group_cache') or not self.__group_cache:
self.__group_cache = self.conn.send_command('servergrouplist')
outlist = {}
for group in self.__group_cache:
outlist[group['keys']['name']] = group['keys']['sgid']
return outlist
def _user_group_list(self, cldbid):
""" List of all groups assigned to a user """
groups = self.conn.send_command('servergroupsbyclientid', {'cldbid': cldbid })
outlist = {}
if type(groups) == list:
for group in groups:
outlist[group['keys']['name']] = group['keys']['sgid']
elif type(groups) == dict:
outlist[groups['keys']['name']] = groups['keys']['sgid']
return outlist
def update_groups(self, uid, groups, character=None):
""" Update the UID's groups based on the provided list """
cldbid = self._get_userid(uid)
if cldbid:
tsgrplist = self._group_list()
usrgrplist = self._user_group_list(cldbid)
# Add groups
if groups.count():
for g in groups:
if not g.name in tsgrplist:
tsgrplist[g.name] = self._create_group(g.name)
if not g.name in usrgrplist:
self.conn.send_command('servergroupaddclient', {'sgid': tsgrplist[g.name], 'cldbid': cldbid })
usrgrplist[g.name] = tsgrplist[g.name]
# Add to corporation groups
if character and character.corporation:
if character.corporation.ticker in usrgrplist:
del usrgrplist[character.corporation.ticker]
else:
if not character.corporation.ticker in tsgrplist:
tsgrplist[character.corporation.ticker] = self._create_group(character.corporation.ticker)
self.conn.send_command('servergroupaddclient', {'sgid': tsgrplist[character.corporation.ticker], 'cldbid': cldbid })
# Remove OKed groups from the delete list
for g in groups:
if g.name in usrgrplist:
del usrgrplist[g.name]
# Remove ignored and admin groups
for k, v in usrgrplist.items():
if not int(v) == self.settings['authed_sgid'] and not int(v) in self.settings['ignore_groups']:
self.conn.send_command('servergroupdelclient', {'sgid': v, 'cldbid': cldbid })
return True
ServiceClass = 'TS3Service'

257
app/sso/services/ts3/ts3.py Normal file
View File

@@ -0,0 +1,257 @@
import time
import socket
import logging
class ConnectionError():
def __init__(self, ip, port):
self.ip = ip
self.port = port
def __str__():
return 'Error connecting to host %s port %s' % (self.ip, self.port)
ts3_escape = { '/': r"\/",
' ': r'\s',
'|': r'\p',
"\a": r'\a',
"\b": r'\b',
"\f": r'\f',
"\n": r'\n',
"\r": r'\r',
"\t": r'\t',
"\v": r'\v' }
class TS3Proto():
bytesin = 0
bytesout = 0
_connected = False
def __init__(self):
self._log = logging.getLogger('%s.%s' % (__name__, self.__class__.__name__))
pass
def connect(self, ip, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect((ip, port))
except:
#raise ConnectionError(ip, port)
raise
else:
self._sock = s
self._sockfile = s.makefile('r', 0)
data = self._sockfile.readline()
if data.strip() == "TS3":
self._sockfile.readline()
self._connected = True
return True
def disconnect(self):
self.send_command("quit")
self._sock.close()
self._sock = None
self._connected = False
self._log.info('Disconnected')
def send_command(self, command, keys=None, opts=None):
cmd = self.construct_command(command, keys=keys, opts=opts)
self.send('%s\n' % cmd)
data = []
while True:
resp = self._sockfile.readline()
resp = self.parse_command(resp)
if not 'command' in resp:
data.append(resp)
else:
break
if resp['command'] == 'error':
if data and resp['keys']['id'] == '0':
if len(data) > 1:
return data
else:
return data[0]
else:
return resp['keys']['id']
def construct_command(self, command, keys=None, opts=None):
"""
Constructs a TS3 formatted command string
Keys can have a single nested list to construct a nested parameter
@param command: Command list
@type command: string
@param keys: Key/Value pairs
@type keys: dict
@param opts: Options
@type opts: list
"""
cstr = [command]
# Add the keys and values, escape as needed
if keys:
for key in keys:
if isinstance(keys[key], list):
ncstr = []
for nest in keys[key]:
ncstr.append("%s=%s" % (key, self._escape_str(nest)))
cstr.append("|".join(ncstr))
else:
cstr.append("%s=%s" % (key, self._escape_str(keys[key])))
# Add in options
if opts:
for opt in opts:
cstr.append("-%s" % opt)
return " ".join(cstr)
def parse_command(self, commandstr):
"""
Parses a TS3 command string into command/keys/opts tuple
@param commandstr: Command string
@type commandstr: string
"""
if len(commandstr.split('|')) > 1:
vals = []
for cmd in commandstr.split('|'):
vals.append(self.parse_command(cmd))
return vals
cmdlist = commandstr.strip().split(' ')
command = None
keys = {}
opts = []
for key in cmdlist:
v = key.strip().split('=')
if len(v) > 1:
# Key
if len > 2:
# Fix the stupidities in TS3 escaping
v = [v[0], '='.join(v[1:])]
key, value = v
keys[key] = self._unescape_str(value)
elif v[0][0] == '-':
# Option
opts.append(v[0][1:])
else:
command = v[0]
d = {'keys': keys, 'opts': opts}
if command:
d['command'] = command
return d
@staticmethod
def _escape_str(value):
"""
Escape a value into a TS3 compatible string
@param value: Value
@type value: string/int
"""
if isinstance(value, int): return "%d" % value
value = value.replace("\\", r'\\')
for i, j in ts3_escape.iteritems():
value = value.replace(i, j)
return value
@staticmethod
def _unescape_str(value):
"""
Unescape a TS3 compatible string into a normal string
@param value: Value
@type value: string/int
"""
if isinstance(value, int): return "%d" % value
value = value.replace(r"\\", "\\")
for i, j in ts3_escape.iteritems():
value = value.replace(j, i)
return value
def send(self, payload):
if self._connected:
self._log.debug('Sent: %s' % payload)
self._sockfile.write(payload)
class TS3Server(TS3Proto):
def __init__(self, ip, port, id=0, sock=None):
"""
Abstraction class for TS3 Servers
@param ip: IP Address
@type ip: str
@param port: Port Number
@type port: int
"""
TS3Proto.__init__(self)
if not sock:
if self.connect(ip, port) and id > 0:
self.use(id)
else:
self._sock = sock
self._sockfile = sock.makefile('r', 0)
self._connected = True
def login(self, username, password):
"""
Login to the TS3 Server
@param username: Username
@type username: str
@param password: Password
@type password: str
"""
d = self.send_command('login', keys={'client_login_name': username, 'client_login_password': password })
if d == 0:
self._log.info('Login Successful')
return True
return False
def serverlist(self):
"""
Get a list of all Virtual Servers on the connected TS3 instance
"""
if self._connected:
return self.send_command('serverlist')
def gm(self, msg):
"""
Send a global message to the current Virtual Server
@param msg: Message
@type ip: str
"""
if self._connected:
return self.send_command('gm', keys={'msg': msg})
def use(self, id):
"""
Use a particular Virtual Server instance
@param id: Virtual Server ID
@type id: int
"""
if self._connected and id > 0:
self.send_command('use', keys={'sid': id})

View File

@@ -0,0 +1,95 @@
import hashlib
import random
from django.db import load_backend, transaction, IntegrityError
from sso.services import BaseDBService
import settings
class MediawikiService(BaseDBService):
"""
Mediawiki Class, allows registration and sign-in
"""
settings = { 'require_user': False,
'require_password': False,
'provide_login': False,
'use_auth_username': False,
'database_name': 'dreddit_wiki' }
default_options = 'rows=80\ncols=50'
SQL_ADD_USER = r"INSERT INTO user (user_name, user_password, user_newpassword, user_email, user_options) VALUES (%s, %s, '', %s, %s)"
SQL_DIS_USER = r"UPDATE user SET user_password = '', user_email = '', user_token = %s WHERE user_name = %s"
SQL_DIS_GROUP = r"INSERT INTO user_groups (ug_user, ug_group) VALUES ((SELECT user_id FROM user WHERE user_name = %s), 'Disabled')"
SQL_ENABLE_USER = r"UPDATE user SET user_password = %s WHERE user_name = %s"
SQL_ENABLE_GROUP = r"DELETE FROM user_groups where ug_user = (SELECT user_id FROM user WHERE user_name = %s) AND ug_group = 'Disabled'"
SQL_CHECK_USER = r"SELECT user_name from user WHERE user_name = %s"
SQL_DEL_REV = r"UPDATE revision SET rev_user = (SELECT user_id FROM user WHERE user_name = 'DeletedUser'), rev_user_text = 'DeletedUser' WHERE rev_user = (SELECT user_id FROM user WHERE user_name = %s)"
SQL_DEL_USER = r"DELETE FROM user WHERE user_name = %s"
def _gen_salt(self):
return "%x" % random.randint(0, 2147483647)
def _gen_mw_hash(self, password, salt=None):
if not salt:
salt = self._gen_salt()
hash = hashlib.md5('%s-%s' % (salt, hashlib.md5(password).hexdigest())).hexdigest()
return ":B:%s:%s" % (salt, hash)
def _gen_user_token(self):
hash = hashlib.md5(self._gen_salt()).hexdigest()
return hash
def _clean_username(self, username):
username = username.strip()
return username[0].upper() + username[1:]
def add_user(self, username, password, **kwargs):
""" Add a user """
if 'user' in kwargs:
email = kwargs['user'].email
else:
email = ''
pwhash = self._gen_mw_hash(password)
self.dbcursor.execute(self.SQL_ADD_USER, [self._clean_username(username), pwhash, email, self.default_options])
return { 'username': self._clean_username(username), 'password': password }
def check_user(self, username):
""" Check if the username exists """
self.dbcursor.execute(self.SQL_CHECK_USER, [self._clean_username(username)])
row = self.dbcursor.fetchone()
if row:
return True
return False
def delete_user(self, uid):
""" Delete a user """
self.dbcursor.execute(self.SQL_DEL_REV, [uid])
self.dbcursor.execute(self.SQL_DEL_USER, [uid])
return True
def disable_user(self, uid):
""" Disable a user """
#self.dbcursor.execute(self.SQL_DIS_USER, [self._gen_user_token(), uid])
try:
self.dbcursor.execute(self.SQL_DIS_GROUP, [uid])
except IntegrityError:
# Record already exists, skip it
pass
return True
def enable_user(self, uid, password):
""" Enable a user """
pwhash = self._gen_mw_hash(password)
self.dbcursor.execute(self.SQL_ENABLE_USER, [pwhash, uid])
self.dbcursor.execute(self.SQL_ENABLE_GROUP, [uid])
return True
def reset_password(self, uid, password):
""" Reset the user's password """
return self.enable_user(uid, password)
ServiceClass = 'MediawikiService'

95
app/sso/tasks.py Normal file
View File

@@ -0,0 +1,95 @@
from celery.decorators import task
from eve_api.models import EVEAccount, EVEPlayerCorporation, EVEPlayerAlliance
from sso.models import ServiceAccount
from django.contrib.auth.models import User
from django.db.models import signals
from utils import installed
# Signals that the tasks need to listen for
def eveapi_deleted(sender, instance, **kwargs):
if instance.user:
update_user_access.delay(user=instance.user.id)
signals.post_delete.connect(eveapi_deleted, sender=EVEAccount)
@task()
def update_user_access(user, **kwargs):
"""
Process all corporate and alliance entries and correct
access groups.
"""
user = User.objects.get(id=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=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 and char.corporation.alliance.group:
chargroups.append(char.corporation.alliance.group)
# Generate the list of groups to add/remove
delgroups = set(set(user.groups.all()) & set(corpgroups)) - set(chargroups)
addgroups = set(chargroups) - set(set(user.groups.all()) & set(corpgroups))
# Check that user's groups fufil requirements
if installed('groups'):
for g in user.groups.filter(groupinformation__parent__isnull=False):
print g
if not g in delgroups and not g.groupinformation.parent in user.groups.all():
delgroups.add(g)
for g in delgroups:
if g in user.groups.all():
user.groups.remove(g)
for g in addgroups:
if not g in user.groups.all():
user.groups.add(g)
# For users set to not active, delete all accounts
if not user.is_active:
for servacc in ServiceAccount.objects.filter(user=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=user):
if not (set(user.groups.all()) & set(servacc.service.groups.all())):
if servacc.active:
servacc.active = 0
servacc.save()
pass
else:
if not servacc.active:
servacc.active = 1
servacc.save()
pass
update_service_groups.delay(user_id=user.id)
@task(ignore_result=True)
def update_service_groups(user_id):
for service in ServiceAccount.objects.filter(user=user_id, active=True).select_related('service__api'):
api = service.service.api_class
try:
api.update_groups(service.service_uid, service.user.groups.all(), service.character)
except:
print "Error updating %s %s" % (service.service, service.service_uid)
pass

View File

View File

@@ -0,0 +1,20 @@
from django import template
from django.conf import settings
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.filter
@stringfilter
def installed(value):
apps = settings.INSTALLED_APPS
if "." in value:
for app in apps:
if app == value:
return True
else:
for app in apps:
fields = app.split(".")
if fields[-1] == value:
return True
return False

View File

@@ -0,0 +1,33 @@
from django import template
register = template.Library()
MOMENT = 120 # duration in seconds within which the time difference
# will be rendered as 'a moment ago'
@register.filter
def naturaltimediff(value):
"""
Finds the difference between the datetime value given and now()
and returns appropriate humanize form
"""
from datetime import datetime
if isinstance(value, datetime):
delta = datetime.now() - value
if delta.days > 6:
return value.strftime("%b %d") # May 15
if delta.days > 1:
return value.strftime("%A") # Wednesday
elif delta.days == 1:
return 'yesterday' # yesterday
elif delta.seconds > 3600:
return str(delta.seconds / 3600 ) + ' hours ago' # 3 hours ago
elif delta.seconds > MOMENT:
return str(delta.seconds/60) + ' minutes ago' # 29 minutes ago
else:
return 'a moment ago' # a moment ago
return defaultfilters.date(value)
else:
return str(value)

23
app/sso/tests.py Normal file
View File

@@ -0,0 +1,23 @@
"""
This file demonstrates two different styles of tests (one doctest and one
unittest). These will both pass when you run "manage.py test".
Replace these with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}

23
app/sso/urls.py Normal file
View File

@@ -0,0 +1,23 @@
from django.conf.urls.defaults import *
from django.core.urlresolvers import reverse
from sso import views
urlpatterns = patterns('',
('^$', views.profile),
(r'^profile/$', views.profile),
(r'^profile/add/service', views.service_add),
(r'^profile/del/service/$', views.service_del),
(r'^profile/del/service/(?P<serviceid>\d+)/$', views.service_del),
(r'^profile/reset/service/(?P<serviceid>\d+)/$', views.service_reset),
(r'^profile/reset/service/(?P<serviceid>\d+)/(?P<accept>\d+)$', views.service_reset),
(r'^profile/apipassword/', views.set_apipasswd),
(r'^profile/refresh/', views.refresh_access),
(r'^profile/refresh/(?P<userid>\d+)/', views.refresh_access),
(r'^users/(?P<username>.*)/$', views.user_view),
(r'^users/$', views.user_lookup),
)
urlpatterns += patterns('django.views.generic.simple',
('^$', 'redirect_to', {'url': reverse('sso.views.profile')}),
)

246
app/sso/views.py Normal file
View File

@@ -0,0 +1,246 @@
import hashlib
import random
import re
import unicodedata
import celery
from django.http import HttpResponse
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.template import RequestContext
from django.core import serializers
from django.conf import settings
from utils import installed
from eve_api.models import EVEAccount, EVEPlayerCharacter
from eve_api.tasks import import_apikey, import_apikey_result, update_user_access
from eve_proxy.models import ApiAccessLog
from sso.models import ServiceAccount, Service, SSOUser, ExistingUser, ServiceError
from sso.forms import UserServiceAccountForm, ServiceAccountResetForm, UserLookupForm, APIPasswordForm
@login_required
def profile(request):
""" Displays the user's profile page """
user = request.user
try:
profile = request.user.get_profile()
except SSOUser.DoesNotExist:
profile = SSOUser(user=request.user)
profile.save()
return render_to_response('sso/profile.html', locals(), context_instance=RequestContext(request))
@login_required
def service_add(request):
""" Add a service to a user's account """
clsform = UserServiceAccountForm(request.user)
if request.method == 'POST':
form = clsform(request.POST)
if form.is_valid():
acc = ServiceAccount(user=request.user, service=form.cleaned_data['service'])
acc.character = form.cleaned_data['character']
if acc.service.settings['require_password']:
if settings.GENERATE_SERVICE_PASSWORD:
acc.password = hashlib.sha1('%s%s%s' % (form.cleaned_data['character'].name, settings.SECRET_KEY, random.randint(0, 2147483647))).hexdigest()
else:
acc.password = form.cleaned_data['password']
else:
acc.password = None
if acc.service.settings['use_auth_username']:
username = request.user.username
else:
# Decode unicode and remove invalid characters
username = re.sub('[^a-zA-Z0-9_-]+', '', unicodedata.normalize('NFKD', acc.character.name).encode('ASCII', 'ignore'))
if acc.service.api_class.check_user(username):
error = "Username already exists on the target service, please contact an admin."
else:
ret = acc.service.api_class.add_user(username, acc.password, user=request.user, character=acc.character)
if ret:
acc.service_uid = ret['username']
acc.save()
error = None
else:
error = "Error creating account on the service, please retry or contact an admin if the error persists."
return render_to_response('sso/serviceaccount/created.html', locals(), context_instance=RequestContext(request))
else:
availserv = Service.objects.filter(groups__in=request.user.groups.all()).exclude(id__in=ServiceAccount.objects.filter(user=request.user).values('service'))
if len(availserv) == 0:
return render_to_response('sso/serviceaccount/noneavailable.html', locals(), context_instance=RequestContext(request))
else:
form = clsform() # An unbound form
return render_to_response('sso/serviceaccount/index.html', locals())
@login_required
def service_del(request, serviceid=0):
""" Delete a service from a user's account """
if serviceid > 0:
try:
acc = ServiceAccount.objects.get(id=serviceid)
except ServiceAccount.DoesNotExist:
return redirect('sso.views.profile')
if not acc.user == request.user:
return redirect('sso.views.profile')
if request.method == 'POST':
if 'confirm-delete' in request.POST:
try:
acc.delete()
except ServiceError:
messages.add_message(request, messages.ERROR, "Error deleting the service account, try again later.")
else:
messages.add_message(request, messages.INFO, "Service account successfully deleted.")
else:
return render_to_response('sso/serviceaccount/deleteconfirm.html', locals(), context_instance=RequestContext(request))
return redirect('sso.views.profile')
@login_required
def service_reset(request, serviceid=0):
""" Reset a user's password on a service """
if serviceid > 0:
try:
acc = ServiceAccount.objects.get(id=serviceid)
except ServiceAccount.DoesNotExist:
return redirect('sso.views.profile')
# If the account is inactive, or the service doesn't require a password, redirect
if not acc.active or ('require_password' in acc.service.settings and not acc.service.settings['require_password']):
return redirect('sso.views.profile')
if acc.user == request.user:
if not request.method == 'POST':
form = ServiceAccountResetForm()
return render_to_response('sso/serviceaccount/reset.html', locals(), context_instance=RequestContext(request))
form = ServiceAccountResetForm(request.POST)
if form.is_valid():
if settings.GENERATE_SERVICE_PASSWORD:
passwd = hashlib.sha1('%s%s%s' % (acc.service_uid, settings.SECRET_KEY, random.randint(0, 2147483647))).hexdigest()
else:
passwd = form.cleaned_data['password']
api = acc.service.api_class
if not api.reset_password(acc.service_uid, passwd):
error = True
return render_to_response('sso/serviceaccount/resetcomplete.html', locals(), context_instance=RequestContext(request))
else:
return render_to_response('sso/serviceaccount/reset.html', locals(), context_instance=RequestContext(request))
return redirect('sso.views.profile')
@login_required
def user_view(request, username=None):
""" View a user's profile as a admin """
if username:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return redirect('sso.views.user_lookup')
else:
return redirect('sso.views.user_lookup')
profile = user.get_profile()
is_admin = request.user.is_staff
if is_admin:
services = ServiceAccount.objects.select_related('service').filter(user=user).only('service__name', 'service_uid', 'active')
characters = EVEPlayerCharacter.objects.select_related('corporation').filter(eveaccount__user=user).only('id', 'name', 'corporation__name')
return render_to_response('sso/lookup/user.html', locals(), context_instance=RequestContext(request))
@login_required
def user_lookup(request):
""" Lookup a user's account by providing a matching criteria """
form = UserLookupForm()
if request.method == 'POST':
form = UserLookupForm(request.POST)
if form.is_valid():
users = None
uids = []
if form.cleaned_data['type'] == '1':
users = User.objects.filter(username__icontains=form.cleaned_data['username']).only('username')
elif form.cleaned_data['type'] == '2':
uid = EVEAccount.objects.filter(characters__name__icontains=form.cleaned_data['username']).values('user')
for u in uid:
uids.append(u['user'])
users = User.objects.filter(id__in=uids).only('username')
elif installed('reddit') and form.cleaned_data['type'] == '3':
from reddit.models import RedditAccount
uid = RedditAccount.objects.filter(username__icontains=form.cleaned_data['username']).values('user')
for u in uid:
uids.append(u['user'])
users = User.objects.filter(id__in=uids).only('username')
elif form.cleaned_data['type'] == '4':
users = User.objects.filter(email__icontains=form.cleaned_data['username']).only('username')
elif form.cleaned_data['type'] == '5':
uids = EVEAccount.objects.filter(id__icontains=form.cleaned_data['username']).values_list('user', flat=True)
users = User.objects.filter(id__in=uids).only('username')
else:
messages.add_message(request, messages.ERROR, "Error parsing form, Type: %s, Value: %s" % (form.cleaned_data['type'], form.cleaned_data['username']))
return redirect('sso.views.user_lookup')
if users and len(users) == 1:
return redirect(user_view, username=users[0].username)
elif users and len(users) > 1:
return render_to_response('sso/lookup/lookuplist.html', locals(), context_instance=RequestContext(request))
else:
messages.add_message(request, messages.INFO, "No results found")
return redirect('sso.views.user_lookup')
return render_to_response('sso/lookup/userlookup.html', locals(), context_instance=RequestContext(request))
@login_required
def set_apipasswd(request):
""" Sets the user's auth API password """
if request.method == 'POST':
form = APIPasswordForm(request.POST)
if form.is_valid():
profile = request.user.get_profile()
profile.api_service_password = hashlib.sha1(form.cleaned_data['password']).hexdigest()
profile.save()
messages.add_message(request, messages.INFO, "Your API Services password has been set.")
return redirect('sso.views.profile') # Redirect after POST
else:
form = APIPasswordForm() # An unbound form
return render_to_response('sso/apipassword.html', locals(), context_instance=RequestContext(request))
@login_required
def refresh_access(request, userid=0):
""" Refreshes the user's access """
if userid > 0 and request.user.is_staff:
update_user_access(userid)
elif request.user:
update_user_access(request.user.id)
messages.add_message(request, messages.INFO, "User access updated.")
return redirect('sso.views.profile')