This commit is contained in:
orbitroom 2016-03-23 01:44:08 -04:00
commit 90a68cda21
28 changed files with 2660 additions and 1158 deletions

View File

@ -60,7 +60,6 @@ Special Permissions In Admin:
auth | user | alliance_member ( Added auto by auth when a member is verified ) auth | user | alliance_member ( Added auto by auth when a member is verified )
auth | user | group_management ( Access to add members to groups within the alliance ) auth | user | group_management ( Access to add members to groups within the alliance )
auth | user | human_resources ( Corp only access to view applications )
auth | user | jabber_broadcast ( Access to broadcast a message over jabber to own groups) auth | user | jabber_broadcast ( Access to broadcast a message over jabber to own groups)
auth | user | jabber_broadcast_all ( Can choose from all groups and the 'all' option when broadcasting) auth | user | jabber_broadcast_all ( Can choose from all groups and the 'all' option when broadcasting)
auth | user | blue_member ( Auto Added to people who register has a blue when adding api key) auth | user | blue_member ( Auto Added to people who register has a blue when adding api key)
@ -75,6 +74,13 @@ Special Permissions In Admin:
auth | user | optimer_view ( Allows for an individual view fleet operations) auth | user | optimer_view ( Allows for an individual view fleet operations)
auth | user | logging_notifications ( Generate notifications from logging) auth | user | logging_notifications ( Generate notifications from logging)
auth | user | human_resources ( View applications to user's corp )
hrapplications | application | delete_application ( Can delete applications )
hrapplications | application | accept_application ( Can accept applications )
hrapplications | application | reject_application ( Can reject applications )
hrapplications | application | view_apis ( Can see applicant's API keys )
hrapplicstions | applicationcomment | add_applicationcomment ( Can comment on applications )
Active Developers Active Developers
Adarnof Adarnof

View File

@ -97,15 +97,6 @@ DATABASES = {
'PASSWORD': os.environ.get('AA_DB_PHPBB3_PASSWORD', 'password'), 'PASSWORD': os.environ.get('AA_DB_PHPBB3_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_PHPBB3_HOST', '127.0.0.1'), 'HOST': os.environ.get('AA_DB_PHPBB3_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_PHPBB3_PORT', '3306'), 'PORT': os.environ.get('AA_DB_PHPBB3_PORT', '3306'),
},
'mumble': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'alliance_mumble',
'USER': os.environ.get('AA_DB_MUMBLE_USER', 'allianceserver'),
'PASSWORD': os.environ.get('AA_DB_MUMBLE_PASSWORD', 'password'),
'HOST': os.environ.get('AA_DB_MUMBLE_HOST', '127.0.0.1'),
'PORT': os.environ.get('AA_DB_MUMBLE_PORT', '3306'),
} }
} }

View File

@ -64,7 +64,9 @@ urlpatterns = patterns('',
# HR Application Management # HR Application Management
url(r'^hr_application_management/', 'hrapplications.views.hr_application_management_view', url(r'^hr_application_management/', 'hrapplications.views.hr_application_management_view',
name="auth_hrapplications_view"), name="auth_hrapplications_view"),
url(r'^hr_application_create/', 'hrapplications.views.hr_application_create_view', url(r'^hr_application_create/$', 'hrapplications.views.hr_application_create_view',
name="auth_hrapplication_create_view"),
url(r'^hr_application_create/(\d+)', 'hrapplications.views.hr_application_create_view',
name="auth_hrapplication_create_view"), name="auth_hrapplication_create_view"),
url(r'^hr_application_remove/(\w+)', 'hrapplications.views.hr_application_remove', url(r'^hr_application_remove/(\w+)', 'hrapplications.views.hr_application_remove',
name="auth_hrapplication_remove"), name="auth_hrapplication_remove"),

View File

@ -0,0 +1 @@
import signals

29
celerytask/signals.py Normal file
View File

@ -0,0 +1,29 @@
from django.db.models.signals import m2m_changed
from django.dispatch import receiver
from django.contrib.auth.models import User
import logging
from .tasks import update_jabber_groups
from .tasks import update_mumble_groups
from .tasks import update_forum_groups
from .tasks import update_ipboard_groups
from .tasks import update_discord_groups
from authentication.models import AuthServicesInfo
logger = logging.getLogger(__name__)
@receiver(m2m_changed, sender=User.groups.through)
def m2m_changed_user_groups(sender, instance, action, *args, **kwargs):
logger.debug("Received m2m_changed from %s groups with action %s" % (instance, action))
if action=="post_add" or action=="post_remove" or action=="post_clear":
logger.debug("Triggering service group update for %s" % instance)
auth, c = AuthServicesInfo.objects.get_or_create(user=instance)
if auth.jabber_username:
update_jabber_groups.delay(instance)
if auth.jabber_username:
update_jabber_groups.delay(instance)
if auth.forum_username:
update_forum_groups.delay(instance)
if auth.ipboard_username:
update_ipboard_groups.delay(instance)
if auth.discord_username:
update_discord_groups.delay(instance)

View File

@ -4,7 +4,6 @@ from django.contrib.auth.models import User
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from notifications import notify from notifications import notify
from celery import task from celery import task
from models import SyncGroupCache
from celery.task.schedules import crontab from celery.task.schedules import crontab
from services.managers.openfire_manager import OpenfireManager from services.managers.openfire_manager import OpenfireManager
from services.managers.mumble_manager import MumbleManager from services.managers.mumble_manager import MumbleManager
@ -31,6 +30,7 @@ from eveonline.models import EveAllianceInfo
from authentication.managers import AuthServicesInfoManager from authentication.managers import AuthServicesInfoManager
from services.models import DiscordAuthToken from services.models import DiscordAuthToken
import evelink
import time import time
import logging import logging
@ -53,68 +53,75 @@ def disable_member(user):
def is_teamspeak3_active(): def is_teamspeak3_active():
return settings.ENABLE_AUTH_TEAMSPEAK3 or settings.ENABLE_BLUE_TEAMSPEAK3 return settings.ENABLE_AUTH_TEAMSPEAK3 or settings.ENABLE_BLUE_TEAMSPEAK3
@task
def update_jabber_groups(user): def update_jabber_groups(user):
logger.debug("Updating jabber groups for user %s" % user) logger.debug("Updating jabber groups for user %s" % user)
syncgroups = SyncGroupCache.objects.filter(user=user)
authserviceinfo = AuthServicesInfo.objects.get(user=user) authserviceinfo = AuthServicesInfo.objects.get(user=user)
groups = [] groups = []
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups))) for group in user.groups.all():
for syncgroup in syncgroups: groups.append(str(group.name))
groups.append(str(syncgroup.groupname))
if len(groups) == 0: if len(groups) == 0:
groups.append('empty') groups.append('empty')
logger.debug("Updating user %s jabber groups to %s" % (user, groups)) logger.debug("Updating user %s jabber groups to %s" % (user, groups))
try:
OpenfireManager.update_user_groups(authserviceinfo.jabber_username, authserviceinfo.jabber_password, groups) OpenfireManager.update_user_groups(authserviceinfo.jabber_username, authserviceinfo.jabber_password, groups)
except:
logger.warn("Jabber group sync failed for %s, retrying in 10 mins" % user)
raise self.retry(countdown = 60 * 10)
logger.debug("Updated user %s jabber groups." % user) logger.debug("Updated user %s jabber groups." % user)
@task
def update_mumble_groups(user): def update_mumble_groups(user):
logger.debug("Updating mumble groups for user %s" % user) logger.debug("Updating mumble groups for user %s" % user)
syncgroups = SyncGroupCache.objects.filter(user=user)
authserviceinfo = AuthServicesInfo.objects.get(user=user) authserviceinfo = AuthServicesInfo.objects.get(user=user)
groups = [] groups = []
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups))) for group in user.groups.all():
for syncgroup in syncgroups: groups.append(str(group.name))
groups.append(str(syncgroup.groupname))
if len(groups) == 0: if len(groups) == 0:
groups.append('empty') groups.append('empty')
logger.debug("Updating user %s mumble groups to %s" % (user, groups)) logger.debug("Updating user %s mumble groups to %s" % (user, groups))
MumbleManager.update_groups(authserviceinfo.mumble_username, groups) try:
MumbleManager.update_groups(authserviceinfo.mumble_username, groups)
except:
logger.warn("Mumble group sync failed for %s, retrying in 10 mins" % user)
raise self.retry(countdown = 60 * 10)
logger.debug("Updated user %s mumble groups." % user) logger.debug("Updated user %s mumble groups." % user)
@task
def update_forum_groups(user): def update_forum_groups(user):
logger.debug("Updating forum groups for user %s" % user) logger.debug("Updating forum groups for user %s" % user)
syncgroups = SyncGroupCache.objects.filter(user=user)
authserviceinfo = AuthServicesInfo.objects.get(user=user) authserviceinfo = AuthServicesInfo.objects.get(user=user)
groups = [] groups = []
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups))) for group in user.groups.all():
for syncgroup in syncgroups: groups.append(str(group.name))
groups.append(str(syncgroup.groupname))
if len(groups) == 0: if len(groups) == 0:
groups.append('empty') groups.append('empty')
logger.debug("Updating user %s forum groups to %s" % (user, groups)) logger.debug("Updating user %s forum groups to %s" % (user, groups))
Phpbb3Manager.update_groups(authserviceinfo.forum_username, groups) try:
Phpbb3Manager.update_groups(authserviceinfo.forum_username, groups)
except:
logger.warn("Phpbb group sync failed for %s, retrying in 10 mins" % user)
raise self.retry(countdown = 60 * 10)
logger.debug("Updated user %s forum groups." % user) logger.debug("Updated user %s forum groups." % user)
@task
def update_ipboard_groups(user): def update_ipboard_groups(user):
logger.debug("Updating user %s ipboard groups." % user) logger.debug("Updating user %s ipboard groups." % user)
syncgroups = SyncGroupCache.objects.filter(user=user)
authserviceinfo = AuthServicesInfo.objects.get(user=user) authserviceinfo = AuthServicesInfo.objects.get(user=user)
groups = [] groups = []
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups))) for group in user.groups.all():
for syncgroup in syncgroups: groups.append(str(group.name))
groups.append(str(syncgroup.groupname))
if len(groups) == 0: if len(groups) == 0:
groups.append('empty') groups.append('empty')
logger.debug("Updating user %s ipboard groups to %s" % (user, groups)) logger.debug("Updating user %s ipboard groups to %s" % (user, groups))
IPBoardManager.update_groups(authserviceinfo.ipboard_username, groups) try:
IPBoardManager.update_groups(authserviceinfo.ipboard_username, groups)
except:
logger.warn("IPBoard group sync failed for %s, retrying in 10 mins" % user)
raise self.retry(countdown = 60 * 10)
logger.debug("Updated user %s ipboard groups." % user) logger.debug("Updated user %s ipboard groups." % user)
@task
def update_teamspeak3_groups(user): def update_teamspeak3_groups(user):
logger.debug("Updating user %s teamspeak3 groups" % user) logger.debug("Updating user %s teamspeak3 groups" % user)
usergroups = user.groups.all() usergroups = user.groups.all()
@ -130,129 +137,23 @@ def update_teamspeak3_groups(user):
Teamspeak3Manager.update_groups(authserviceinfo.teamspeak3_uid, groups) Teamspeak3Manager.update_groups(authserviceinfo.teamspeak3_uid, groups)
logger.debug("Updated user %s teamspeak3 groups." % user) logger.debug("Updated user %s teamspeak3 groups." % user)
@task
def update_discord_groups(user): def update_discord_groups(user):
logger.debug("Updating discord groups for user %s" % user) logger.debug("Updating discord groups for user %s" % user)
syncgroups = SyncGroupCache.objects.filter(user=user)
authserviceinfo = AuthServicesInfo.objects.get(user=user) authserviceinfo = AuthServicesInfo.objects.get(user=user)
groups = [] for group in user.groups.all():
for syncgroup in syncgroups: groups.append(str(group.name))
groups.append(str(syncgroup.groupname))
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups)))
if len(groups) == 0: if len(groups) == 0:
logger.debug("No syncgroups found for user. Adding empty group.") logger.debug("No syncgroups found for user. Adding empty group.")
groups.append('empty') groups.append('empty')
logger.debug("Updating user %s discord groups to %s" % (user, groups)) logger.debug("Updating user %s discord groups to %s" % (user, groups))
DiscordManager.update_groups(authserviceinfo.discord_uid, groups) try:
DiscordManager.update_groups(authserviceinfo.discord_uid, groups)
except:
logger.warn("Discord group sync failed for %s, retrying in 10 mins" % user)
raise self.retry(countdown = 60 * 10)
logger.debug("Updated user %s discord groups." % user) logger.debug("Updated user %s discord groups." % user)
def create_syncgroup_for_user(user, groupname, servicename):
logger.debug("Creating syncgroupcache for user %s group %s in service %s" % (user, groupname, servicename))
synccache = SyncGroupCache()
synccache.groupname = groupname
synccache.user = user
synccache.servicename = servicename
synccache.save()
logger.info("Created syncgroup for user %s group %s in service %s" % (user, groupname, servicename))
def remove_all_syncgroups_for_service(user, servicename):
logger.debug("Removing all syncgroups for user %s service %s" % (user, servicename))
syncgroups = SyncGroupCache.objects.filter(user=user)
logger.debug("User %s has %s syncgroups." % (user, len(syncgroups)))
for syncgroup in syncgroups:
if syncgroup.servicename == servicename:
logger.debug("Deleting syncgroups %s" % syncgroup)
syncgroup.delete()
logger.info("Removed all syncgroups for user %s service %s" % (user, servicename))
def add_to_databases(user, groups, syncgroups):
logger.debug("add_to_database for user %s called. groups %s - syncgroups %s" % (user, groups, syncgroups))
authserviceinfo = None
try:
authserviceinfo = AuthServicesInfo.objects.get(user=user)
logger.debug("Got authservicesinfo object %s" % authserviceinfo)
except:
logger.debug("No authservicesinfo object found for user %s" % user)
pass
if authserviceinfo:
authserviceinfo = AuthServicesInfo.objects.get(user=user)
if authserviceinfo.teamspeak3_uid and authserviceinfo.teamspeak3_uid != "":
logger.debug("Updating user TS groups.")
update_teamspeak3_groups(user)
for group in groups:
if authserviceinfo.jabber_username and authserviceinfo.jabber_username != "":
if syncgroups.filter(groupname=group.name).filter(servicename="openfire").exists() is not True:
logger.debug("User %s has jabber username %s - missing group %s." % (user, authserviceinfo.jabber_username, group.name))
create_syncgroup_for_user(user, group.name, "openfire")
update_jabber_groups(user)
if authserviceinfo.mumble_username and authserviceinfo.mumble_username != "":
if syncgroups.filter(groupname=group.name).filter(servicename="mumble").exists() is not True:
logger.debug("User %s has mumble username %s - missing group %s." % (user, authserviceinfo.mumble_username, group.name))
create_syncgroup_for_user(user, group.name, "mumble")
update_mumble_groups(user)
if authserviceinfo.forum_username and authserviceinfo.forum_username != "":
if syncgroups.filter(groupname=group.name).filter(servicename="phpbb3").exists() is not True:
logger.debug("User %s has phpbb username %s - missing group %s." % (user, authserviceinfo.forum_username, group.name))
create_syncgroup_for_user(user, group.name, "phpbb3")
update_forum_groups(user)
if authserviceinfo.ipboard_username and authserviceinfo.ipboard_username != "":
if syncgroups.filter(groupname=group.name).filter(servicename="ipboard").exists() is not True:
logger.debug("User %s has ipboard username %s - missing group %s." % (user, authserviceinfo.ipboard_username, group.name))
create_syncgroup_for_user(user, group.name, "ipboard")
update_ipboard_groups(user)
if authserviceinfo.discord_uid and authserviceinfo.discord_uid != "":
if syncgroups.filter(groupname=group.name).filter(servicename="discord").exists() is not True:
logger.debug("User %s has discord uid %s - missing group %s." % (user, authserviceinfo.discord_uid, group.name))
create_syncgroup_for_user(user, group.name, "discord")
update_discord_groups(user)
def remove_from_databases(user, groups, syncgroups):
logger.debug("remove_from_database for user %s called. groups %s - syncgroups %s" % (user, groups, syncgroups))
authserviceinfo = None
try:
authserviceinfo = AuthServicesInfo.objects.get(user=user)
logger.debug("Got authservicesinfo object %s" % authserviceinfo)
except:
logger.debug("No authservicesinfo object found for user %s" % user)
pass
if authserviceinfo:
update = False
for syncgroup in syncgroups:
group = groups.filter(name=syncgroup.groupname)
logger.debug("Got group %s for syncgroup %s" % (group, syncgroup))
if not group:
logger.debug("Deleting syncgroup %s" % syncgroup)
syncgroup.delete()
update = True
if update:
logger.debug("Syncgroups updated. Propogating to services for user %s" % user)
if authserviceinfo.jabber_username and authserviceinfo.jabber_username != "":
logger.debug("User %s has jabber username %s - updating groups." % (user, authserviceinfo.jabber_username))
update_jabber_groups(user)
if authserviceinfo.mumble_username and authserviceinfo.mumble_username != "":
logger.debug("User %s has mumble username %s - updating groups." % (user, authserviceinfo.mumble_username))
update_mumble_groups(user)
if authserviceinfo.forum_username and authserviceinfo.forum_username != "":
logger.debug("User %s has forum username %s - updating groups." % (user, authserviceinfo.forum_username))
update_forum_groups(user)
if authserviceinfo.ipboard_username and authserviceinfo.ipboard_username != "":
logger.debug("User %s has ipboard username %s - updating groups." % (user, authserviceinfo.ipboard_username))
update_ipboard_groups(user)
if authserviceinfo.teamspeak3_uid and authserviceinfo.teamspeak3_uid != "":
logger.debug("User %s has ts3 uid %s - updating groups." % (user, authserviceinfo.teamspeak3_uid))
update_teamspeak3_groups(user)
if authserviceinfo.discord_uid and authserviceinfo.discord_uid != "":
logger.debug("User %s has discord uid %s - updating groups." % (user, authserviceinfo.discord_uid))
update_discord_groups(user)
def assign_corp_group(auth): def assign_corp_group(auth):
corp_group = None corp_group = None
if auth.main_char_id: if auth.main_char_id:
@ -420,24 +321,6 @@ def set_state(user):
if change: if change:
notify(user, "Membership State Change", message="You membership state has been changed to %s" % state) notify(user, "Membership State Change", message="You membership state has been changed to %s" % state)
# Run every minute
@periodic_task(run_every=crontab(minute="*/10"))
def run_databaseUpdate():
logger.debug("Starting database update.")
users = User.objects.all()
if (is_teamspeak3_active()):
logger.debug("TS3 installed. Syncing local group objects.")
Teamspeak3Manager._sync_ts_group_db()
for user in users:
logger.debug("Initiating database update for user %s" % user)
groups = user.groups.all()
logger.debug("User has groups %s" % groups)
syncgroups = SyncGroupCache.objects.filter(user=user)
logger.debug("User has syncgroups %s" % syncgroups)
add_to_databases(user, groups, syncgroups)
remove_from_databases(user, groups, syncgroups)
time.sleep(1)
# Run every 2 hours # Run every 2 hours
@periodic_task(run_every=crontab(minute="0", hour="*/2")) @periodic_task(run_every=crontab(minute="0", hour="*/2"))
def run_discord_token_cleanup(): def run_discord_token_cleanup():
@ -460,76 +343,43 @@ def refresh_api(api_key_pair):
state = determine_membership_by_user(user) state = determine_membership_by_user(user)
if state == "BLUE": if state == "BLUE":
if settings.BLUE_API_ACCOUNT: if settings.BLUE_API_ACCOUNT:
type = EveApiManager.check_api_is_type_account(api_key_pair.api_id, api_key_pair.api_key) if not EveApiManager.check_api_is_type_account(api_key_pair.api_id, api_key_pair.api_key):
if type == None:
api_key_pair.error_count += 1
api_key_pair.save()
logger.info("API key %s incurred an error checking if type account. Error count is now %s" % (api_key_pair.api_id, api_key_pair.error_count))
still_valid = None
elif type == False:
logger.info("Determined api key %s for blue user %s is no longer type account as requred." % (api_key_pair.api_id, user)) logger.info("Determined api key %s for blue user %s is no longer type account as requred." % (api_key_pair.api_id, user))
still_valid = False still_valid = False
notify(user, "API Failed Validation", message="Your API key ID %s is not account-wide as required." % api_key_pair.api_id, level="danger") notify(user, "API Failed Validation", message="Your API key ID %s is not account-wide as required." % api_key_pair.api_id, level="danger")
full = EveApiManager.check_blue_api_is_full(api_key_pair.api_id, api_key_pair.api_key) if not EveApiManager.check_blue_api_is_full(api_key_pair.api_id, api_key_pair.api_key):
if full == None:
api_key_pair.error_count += 1
api_key_pair.save()
logger.info("API key %s incurred an error checking if meets mask requirements. Error count is now %s" % (api_key_pair.api_id, api_key_pair.error_count))
still_valid = None
elif full == False:
logger.info("Determined api key %s for blue user %s no longer meets minimum access mask as required." % (api_key_pair.api_id, user)) logger.info("Determined api key %s for blue user %s no longer meets minimum access mask as required." % (api_key_pair.api_id, user))
still_valid = False still_valid = False
notify(user, "API Failed Validation", message="Your API key ID %s does not meet access mask requirements." % api_key_pair.api_id, level="danger") notify(user, "API Failed Validation", message="Your API key ID %s does not meet access mask requirements." % api_key_pair.api_id, level="danger")
elif state == "MEMBER": elif state == "MEMBER":
if settings.MEMBER_API_ACCOUNT: if settings.MEMBER_API_ACCOUNT:
type = EveApiManager.check_api_is_type_account(api_key_pair.api_id, api_key_pair.api_key) if not EveApiManager.check_api_is_type_account(api_key_pair.api_id, api_key_pair.api_key):
if type == None:
api_key_pair.error_count += 1
api_key_pair.save()
logger.info("API key %s incurred an error checking if type account. Error count is now %s" % (api_key_pair.api_id, api_key_pair.error_count))
still_valid = None
elif type == False:
logger.info("Determined api key %s for user %s is no longer type account as required." % (api_key_pair.api_id, user)) logger.info("Determined api key %s for user %s is no longer type account as required." % (api_key_pair.api_id, user))
still_valid = False still_valid = False
notify(user, "API Failed Validation", message="Your API key ID %s is not account-wide as required." % api_key_pair.api_id, level="danger") notify(user, "API Failed Validation", message="Your API key ID %s is not account-wide as required." % api_key_pair.api_id, level="danger")
full = EveApiManager.check_api_is_full(api_key_pair.api_id, api_key_pair.api_key) if not EveApiManager.check_api_is_full(api_key_pair.api_id, api_key_pair.api_key):
if full == None:
api_key_pair.error_count += 1
api_key_pair.save()
logger.info("API key %s incurred an error checking if meets mask requirements. Error count is now %s" % (api_key_pair.api_id, api_key_pair.error_count))
still_valid = None
elif full == False:
logger.info("Determined api key %s for user %s no longer meets minimum access mask as required." % (api_key_pair.api_id, user)) logger.info("Determined api key %s for user %s no longer meets minimum access mask as required." % (api_key_pair.api_id, user))
still_valid = False still_valid = False
notify(user, "API Failed Validation", message="Your API key ID %s does not meet access mask requirements." % api_key_pair.api_id, level="danger") notify(user, "API Failed Validation", message="Your API key ID %s does not meet access mask requirements." % api_key_pair.api_id, level="danger")
if still_valid == None: if not still_valid:
if api_key_pair.error_count >= 3: logger.debug("API key %s has failed validation; it and its characters will be deleted." % api_key_pair.api_id)
logger.info("API key %s has incurred 3 or more errors. Assuming invalid." % api_key_pair.api_id) EveManager.delete_characters_by_api_id(api_key_pair.api_id, user.id)
still_valid = False EveManager.delete_api_key_pair(api_key_pair.api_id, user.id)
notify(user, "API Failed Validation", message="Your API key ID %s has accumulated too many errors during refresh and is assumed to be invalid." % api_key_pair.api_id, level="danger") notify(user, "API Key Deleted", message="Your API key ID %s has failed validation. It and its associated characters have been deleted." % api_key_pair.api_id, level="danger")
if still_valid == False: else:
logger.debug("API key %s has failed validation; it and its characters will be deleted." % api_key_pair.api_id) logger.info("Determined api key %s still meets requirements." % api_key_pair.api_id)
EveManager.delete_characters_by_api_id(api_key_pair.api_id, user.id) # Update characters
EveManager.delete_api_key_pair(api_key_pair.api_id, user.id) characters = EveApiManager.get_characters_from_api(api_key_pair.api_id, api_key_pair.api_key)
notify(user, "API Key Deleted", message="Your API key ID %s has failed validation. It and its associated characters have been deleted." % api_key_pair.api_id, level="danger") EveManager.update_characters_from_list(characters)
elif still_valid == True: new_character = False
if api_key_pair.error_count != 0: for char in characters.result:
logger.info("Clearing error count for api %s as it passed validation" % api_key_pair.api_id) # Ensure we have a model for all characters on key
api_key_pair.error_count = 0 if not EveManager.check_if_character_exist(characters.result[char]['name']):
api_key_pair.save() new_character = True
logger.info("Determined api key %s still meets requirements." % api_key_pair.api_id) logger.debug("API key %s has a new character on the account: %s" % (api_key_pair.api_id, characters.result[char]['name']))
# Update characters if new_character:
characters = EveApiManager.get_characters_from_api(api_key_pair.api_id, api_key_pair.api_key) logger.debug("Creating new character %s from api key %s" % (characters.result[char]['name'], api_key_pair.api_id))
EveManager.update_characters_from_list(characters) EveManager.create_characters_from_list(characters, user, api_key_pair.api_id)
new_character = False
for char in characters.result:
# Ensure we have a model for all characters on key
if not EveManager.check_if_character_exist(characters.result[char]['name']):
new_character = True
logger.debug("API key %s has a new character on the account: %s" % (api_key_pair.api_id, characters.result[char]['name']))
if new_character:
logger.debug("Creating new character %s from api key %s" % (characters.result[char]['name'], api_key_pair.api_id))
EveManager.create_characters_from_list(characters, user, api_key_pair.api_id)
else: else:
logger.debug("API key %s is no longer valid; it and its characters will be deleted." % api_key_pair.api_id) logger.debug("API key %s is no longer valid; it and its characters will be deleted." % api_key_pair.api_id)
EveManager.delete_characters_by_api_id(api_key_pair.api_id, user.id) EveManager.delete_characters_by_api_id(api_key_pair.api_id, user.id)
@ -551,7 +401,19 @@ def run_api_refresh():
authserviceinfo, c = AuthServicesInfo.objects.get_or_create(user=user) authserviceinfo, c = AuthServicesInfo.objects.get_or_create(user=user)
logger.debug("User %s has api keys. Proceeding to refresh." % user) logger.debug("User %s has api keys. Proceeding to refresh." % user)
for api_key_pair in api_key_pairs: for api_key_pair in api_key_pairs:
refresh_api(api_key_pair) try:
refresh_api(api_key_pair)
except evelink.api.APIError as e:
if int(e.code) >= 500:
logger.error("EVE API servers encountered an error. Aborting API updates")
return
elif int(e.code) == 221:
logger.warn("API server hiccup while updating %s" % api_key_pair)
else:
logger.debug("API key %s failed update with error code %s" % (api_key_pair.api_id, e.code))
EveManager.delete_characters_by_api_id(api_key_pair.api_id, user.id)
EveManager.delete_api_key_pair(api_key_pair.api_id, user.id)
notify(user, "API Key Deleted", message="Your API key ID %s failed validation with code %s. It and its associated characters have been deleted." % (api_key_pair.api_id, e.code), level="danger")
# Check our main character # Check our main character
if EveCharacter.objects.filter(character_id=authserviceinfo.main_char_id).exists() is False: if EveCharacter.objects.filter(character_id=authserviceinfo.main_char_id).exists() is False:
logger.info("User %s main character id %s missing model. Clearning main character." % (user, authserviceinfo.main_char_id)) logger.info("User %s main character id %s missing model. Clearning main character." % (user, authserviceinfo.main_char_id))
@ -625,178 +487,180 @@ def run_corp_update():
logger.warn("Aborted updating corp and alliance models: API server unreachable") logger.warn("Aborted updating corp and alliance models: API server unreachable")
return return
standing_level = 'alliance' standing_level = 'alliance'
try:
# get corp info for owning corp if required
ownercorpinfo = {}
if settings.IS_CORP:
standing_level = 'corp'
logger.debug("Getting information for owning corp with id %s" % settings.CORP_ID)
ownercorpinfo = EveApiManager.get_corporation_information(settings.CORP_ID)
if not ownercorpinfo:
logger.error("Failed to retrieve corp info for owning corp id %s - bad corp id?" % settings.CORP_ID)
return
# get corp info for owning corp if required # check if we need to update an alliance model
ownercorpinfo = {} alliance_id = ''
if settings.IS_CORP: if ownercorpinfo and ownercorpinfo['alliance']['id']:
standing_level = 'corp' alliance_id = ownercorpinfo['alliance']['id']
logger.debug("Getting information for owning corp with id %s" % settings.CORP_ID) elif settings.IS_CORP is False:
ownercorpinfo = EveApiManager.get_corporation_information(settings.CORP_ID) alliance_id = settings.ALLIANCE_ID
if not ownercorpinfo:
logger.error("Failed to retrieve corp info for owning corp id %s - bad corp id?" % settings.CORP_ID)
return
# check if we need to update an alliance model # get and create alliance info for owning alliance if required
alliance_id = '' alliance = None
if ownercorpinfo and ownercorpinfo['alliance']['id']: if alliance_id:
alliance_id = ownercorpinfo['alliance']['id'] logger.debug("Getting information for owning alliance with id %s" % alliance_id)
elif settings.IS_CORP is False: ownerallianceinfo = EveApiManager.get_alliance_information(alliance_id)
alliance_id = settings.ALLIANCE_ID if not ownerallianceinfo:
logger.error("Failed to retrieve corp info for owning alliance id %s - bad alliance id?" % alliance_id)
return
if EveAllianceInfo.objects.filter(alliance_id=ownerallianceinfo['id']).exists():
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
EveManager.update_alliance_info(ownerallianceinfo['id'], ownerallianceinfo['executor_id'], ownerallianceinfo['member_count'], False)
else:
populate_alliance(alliance_id)
alliance = EveAllianceInfo.objects.get(alliance_id=alliance_id)
# get and create alliance info for owning alliance if required # create corp info for owning corp if required
alliance = None if ownercorpinfo:
if alliance_id: if EveCorporationInfo.objects.filter(corporation_id=ownercorpinfo['id']).exists():
logger.debug("Getting information for owning alliance with id %s" % alliance_id) logger.debug("Updating existing owner corp model with id %s" % ownercorpinfo['id'])
ownerallianceinfo = EveApiManager.get_alliance_information(alliance_id) EveManager.update_corporation_info(ownercorpinfo['id'], ownercorpinfo['members']['current'], alliance, False)
if not ownerallianceinfo: else:
logger.error("Failed to retrieve corp info for owning alliance id %s - bad alliance id?" % alliance_id) logger.info("Creating model for owning corp with id %s" % ownercorpinfo['id'])
return EveManager.create_corporation_info(ownercorpinfo['id'], ownercorpinfo['name'], ownercorpinfo['ticker'],
if EveAllianceInfo.objects.filter(alliance_id=ownerallianceinfo['id']).exists():
logger.debug("Updating existing owner alliance model with id %s" % alliance_id)
EveManager.update_alliance_info(ownerallianceinfo['id'], ownerallianceinfo['executor_id'], ownerallianceinfo['member_count'], False)
else:
populate_alliance(alliance_id)
alliance = EveAllianceInfo.objects.get(alliance_id=alliance_id)
# create corp info for owning corp if required
if ownercorpinfo:
if EveCorporationInfo.objects.filter(corporation_id=ownercorpinfo['id']).exists():
logger.debug("Updating existing owner corp model with id %s" % ownercorpinfo['id'])
EveManager.update_corporation_info(ownercorpinfo['id'], ownercorpinfo['members']['current'], alliance, False)
else:
logger.info("Creating model for owning corp with id %s" % ownercorpinfo['id'])
EveManager.create_corporation_info(ownercorpinfo['id'], ownercorpinfo['name'], ownercorpinfo['ticker'],
ownercorpinfo['members']['current'], False, alliance) ownercorpinfo['members']['current'], False, alliance)
# validate and create corp models for member corps of owning alliance # validate and create corp models for member corps of owning alliance
if alliance: if alliance:
current_corps = EveCorporationInfo.objects.filter(alliance=alliance) current_corps = EveCorporationInfo.objects.filter(alliance=alliance)
for corp in current_corps: for corp in current_corps:
if corp.corporation_id in ownerallianceinfo['member_corps'] is False: if corp.corporation_id in ownerallianceinfo['member_corps'] is False:
logger.info("Corp %s is no longer in owning alliance %s - updating model." % (corp, alliance)) logger.info("Corp %s is no longer in owning alliance %s - updating model." % (corp, alliance))
corp.alliance = None corp.alliance = None
corp.save()
for member_corp in ownerallianceinfo['member_corps']:
if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
if corp.alliance == alliance is not True:
logger.info("Associating corp %s with owning alliance %s" % (corp, alliance))
corp.alliance = alliance
corp.save() corp.save()
else: for member_corp in ownerallianceinfo['member_corps']:
corpinfo = EveApiManager.get_corporation_information(member_corp) if EveCorporationInfo.objects.filter(corporation_id=member_corp).exists():
logger.info("Creating model for owning alliance member corp with id %s" % corpinfo['id']) corp = EveCorporationInfo.objects.get(corporation_id=member_corp)
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'], if corp.alliance == alliance is not True:
logger.info("Associating corp %s with owning alliance %s" % (corp, alliance))
corp.alliance = alliance
corp.save()
else:
corpinfo = EveApiManager.get_corporation_information(member_corp)
logger.info("Creating model for owning alliance member corp with id %s" % corpinfo['id'])
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
corpinfo['members']['current'], False, alliance) corpinfo['members']['current'], False, alliance)
# update existing corp models # update existing corp models
for corp in EveCorporationInfo.objects.all(): for corp in EveCorporationInfo.objects.all():
update_corp.delay(corp.corporation_id) update_corp.delay(corp.corporation_id)
# update existing alliance models # update existing alliance models
for alliance in EveAllianceInfo.objects.all(): for alliance in EveAllianceInfo.objects.all():
update_alliance.delay(alliance.alliance_id) update_alliance.delay(alliance.alliance_id)
# create standings # create standings
standings = EveApiManager.get_corp_standings() standings = EveApiManager.get_corp_standings()
if standings: if standings:
standings = standings[standing_level] standings = standings[standing_level]
for standing in standings: for standing in standings:
if int(standings[standing]['standing']) >= settings.BLUE_STANDING: if int(standings[standing]['standing']) >= settings.BLUE_STANDING:
logger.debug("Standing %s meets threshold" % standing) logger.debug("Standing %s meets threshold" % standing)
if EveApiManager.check_if_id_is_alliance(standing): if EveApiManager.check_if_id_is_alliance(standing):
logger.debug("Standing %s is an alliance" % standing) logger.debug("Standing %s is an alliance" % standing)
if EveAllianceInfo.objects.filter(alliance_id=standing).exists(): if EveAllianceInfo.objects.filter(alliance_id=standing).exists():
alliance = EveAllianceInfo.objects.get(alliance_id=standing) alliance = EveAllianceInfo.objects.get(alliance_id=standing)
if alliance.is_blue is not True: if alliance.is_blue is not True:
logger.info("Updating alliance %s as blue" % alliance) logger.info("Updating alliance %s as blue" % alliance)
alliance.is_blue = True alliance.is_blue = True
alliance.save() alliance.save()
else: else:
populate_alliance(standing, blue=True) populate_alliance(standing, blue=True)
elif EveApiManager.check_if_id_is_corp(standing): elif EveApiManager.check_if_id_is_corp(standing):
logger.debug("Standing %s is a corp" % standing) logger.debug("Standing %s is a corp" % standing)
if EveCorporationInfo.objects.filter(corporation_id=standing).exists(): if EveCorporationInfo.objects.filter(corporation_id=standing).exists():
corp = EveCorporationInfo.objects.get(corporation_id=standing) corp = EveCorporationInfo.objects.get(corporation_id=standing)
if corp.is_blue is not True: if corp.is_blue is not True:
logger.info("Updating corp %s as blue" % corp) logger.info("Updating corp %s as blue" % corp)
corp.is_blue = True corp.is_blue = True
corp.save() corp.save()
else: else:
logger.info("Creating model for blue corp with id %s" % standing) logger.info("Creating model for blue corp with id %s" % standing)
corpinfo = EveApiManager.get_corporation_information(standing) corpinfo = EveApiManager.get_corporation_information(standing)
corp_alliance = None corp_alliance = None
if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists(): if EveAllianceInfo.objects.filter(alliance_id=corpinfo['alliance']['id']).exists():
logger.debug("New corp model for standing %s has existing alliance model" % standing) logger.debug("New corp model for standing %s has existing alliance model" % standing)
corp_alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id']) corp_alliance = EveAllianceInfo.objects.get(alliance_id=corpinfo['alliance']['id'])
EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'], EveManager.create_corporation_info(corpinfo['id'], corpinfo['name'], corpinfo['ticker'],
corpinfo['members']['current'], True, corp_alliance) corpinfo['members']['current'], True, corp_alliance)
# update alliance standings # update alliance standings
for alliance in EveAllianceInfo.objects.filter(is_blue=True): for alliance in EveAllianceInfo.objects.filter(is_blue=True):
if int(alliance.alliance_id) in standings: if int(alliance.alliance_id) in standings:
if float(standings[int(alliance.alliance_id)]['standing']) < float(settings.BLUE_STANDING): if float(standings[int(alliance.alliance_id)]['standing']) < float(settings.BLUE_STANDING):
logger.info("Alliance %s no longer meets minimum blue standing threshold" % alliance) logger.info("Alliance %s no longer meets minimum blue standing threshold" % alliance)
alliance.is_blue = False
alliance.save()
else:
logger.info("Alliance %s no longer in standings" % alliance)
alliance.is_blue = False alliance.is_blue = False
alliance.save() alliance.save()
else:
logger.info("Alliance %s no longer in standings" % alliance)
alliance.is_blue = False
alliance.save()
# update corp standings # update corp standings
for corp in EveCorporationInfo.objects.filter(is_blue=True): for corp in EveCorporationInfo.objects.filter(is_blue=True):
if int(corp.corporation_id) in standings: if int(corp.corporation_id) in standings:
if float(standings[int(corp.corporation_id)]['standing']) < float(settings.BLUE_STANDING): if float(standings[int(corp.corporation_id)]['standing']) < float(settings.BLUE_STANDING):
logger.info("Corp %s no longer meets minimum blue standing threshold" % corp) logger.info("Corp %s no longer meets minimum blue standing threshold" % corp)
corp.is_blue = False
corp.save()
else:
if corp.alliance:
if corp.alliance.is_blue is False:
logger.info("Corp %s and its alliance %s are no longer blue" % (corp, corp.alliance))
corp.is_blue = False corp.is_blue = False
corp.save() corp.save()
else: else:
logger.info("Corp %s is no longer blue" % corp) if corp.alliance:
corp.is_blue = False if corp.alliance.is_blue is False:
corp.save() logger.info("Corp %s and its alliance %s are no longer blue" % (corp, corp.alliance))
corp.is_blue = False
corp.save()
else:
logger.info("Corp %s is no longer blue" % corp)
corp.is_blue = False
corp.save()
# delete unnecessary alliance models # delete unnecessary alliance models
for alliance in EveAllianceInfo.objects.filter(is_blue=False): for alliance in EveAllianceInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete alliance %s" % alliance) logger.debug("Checking to delete alliance %s" % alliance)
if settings.IS_CORP is False: if settings.IS_CORP is False:
if alliance.alliance_id == settings.ALLIANCE_ID is False: if alliance.alliance_id == settings.ALLIANCE_ID is False:
logger.info("Deleting unnecessary alliance model %s" % alliance) logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete() alliance.delete()
else:
if alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists() is False:
logger.info("Deleting unnecessary alliance model %s" % alliance)
alliance.delete()
# delete unnecessary corp models
for corp in EveCorporationInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete corp %s" % corp)
if settings.IS_CORP is False:
if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if corp.alliance.alliance_id == settings.ALLIANCE_ID is False:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else: else:
logger.info("Deleting unnecessary corp model %s" % corp) if alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists() is False:
corp.delete() logger.info("Deleting unnecessary alliance model %s" % alliance)
else: alliance.delete()
if corp.corporation_id != settings.CORP_ID:
logger.debug("Corp %s is not owning corp" % corp) # delete unnecessary corp models
for corp in EveCorporationInfo.objects.filter(is_blue=False):
logger.debug("Checking to delete corp %s" % corp)
if settings.IS_CORP is False:
if corp.alliance: if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance)) logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if corp.alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists() is False: if corp.alliance.alliance_id == settings.ALLIANCE_ID is False:
logger.info("Deleting unnecessary corp model %s" % corp) logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete() corp.delete()
else: else:
logger.info("Deleting unnecessary corp model %s" % corp) logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete() corp.delete()
else: else:
logger.debug("Corp %s is owning corp" % corp) if corp.corporation_id != settings.CORP_ID:
logger.debug("Corp %s is not owning corp" % corp)
if corp.alliance:
logger.debug("Corp %s has alliance %s" % (corp, corp.alliance))
if corp.alliance.evecorporationinfo_set.filter(corporation_id=settings.CORP_ID).exists() is False:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
logger.info("Deleting unnecessary corp model %s" % corp)
corp.delete()
else:
logger.debug("Corp %s is owning corp" % corp)
except evelink.api.APIError as e:
logger.error("Model update failed with error code %s" % e.code)

View File

@ -4,6 +4,7 @@ from django.conf import settings
from services.managers.eve_api_manager import EveApiManager from services.managers.eve_api_manager import EveApiManager
from eveonline.managers import EveManager from eveonline.managers import EveManager
from eveonline.models import EveCharacter from eveonline.models import EveCharacter
import evelink
from celerytask.tasks import determine_membership_by_character from celerytask.tasks import determine_membership_by_character
@ -28,27 +29,34 @@ class UpdateKeyForm(forms.Form):
super(UpdateKeyForm, self).clean() super(UpdateKeyForm, self).clean()
if 'api_id' in self.cleaned_data and 'api_key' in self.cleaned_data: if 'api_id' in self.cleaned_data and 'api_key' in self.cleaned_data:
if EveManager.check_if_api_key_pair_exist(self.cleaned_data['api_id']): try:
logger.debug("UpdateKeyForm failed cleaning as API id %s already exists." % self.cleaned_data['api_id']) if EveManager.check_if_api_key_pair_exist(self.cleaned_data['api_id']):
raise forms.ValidationError(u'API key already exist') logger.debug("UpdateKeyForm failed cleaning as API id %s already exists." % self.cleaned_data['api_id'])
if EveApiManager.api_key_is_valid(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False: raise forms.ValidationError(u'API key already exist')
raise forms.ValidationError(u'API key is invalid') if EveApiManager.api_key_is_valid(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False:
chars = EveApiManager.get_characters_from_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']).result raise forms.ValidationError(u'API key is invalid')
states = [] chars = EveApiManager.get_characters_from_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']).result
states.append(self.user_state) states = []
for char in chars: states.append(self.user_state)
evechar = EveCharacter() for char in chars:
evechar.character_name = chars[char]['name'] evechar = EveCharacter()
evechar.corporation_id = chars[char]['corp']['id'] evechar.character_name = chars[char]['name']
evechar.alliance_id = chars[char]['alliance']['id'] evechar.corporation_id = chars[char]['corp']['id']
state = determine_membership_by_character(evechar) evechar.alliance_id = chars[char]['alliance']['id']
logger.debug("API ID %s character %s has state %s" % (self.cleaned_data['api_id'], evechar, state)) state = determine_membership_by_character(evechar)
states.append(state) logger.debug("API ID %s character %s has state %s" % (self.cleaned_data['api_id'], evechar, state))
states.append(state)
if 'MEMBER' in states: if 'MEMBER' in states:
if EveApiManager.validate_member_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False: if EveApiManager.validate_member_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False:
raise forms.ValidationError(u'API must meet member requirements') raise forms.ValidationError(u'API must meet member requirements')
if 'BLUE' in states: if 'BLUE' in states:
if EveApiManager.validate_blue_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False: if EveApiManager.validate_blue_api(self.cleaned_data['api_id'], self.cleaned_data['api_key']) is False:
raise forms.ValidationError(u'API must meet blue requirements') raise forms.ValidationError(u'API must meet blue requirements')
return self.cleaned_data return self.cleaned_data
except evelink.api.APIError as e:
logger.debug("Got error code %s while validating API %s" % (e.code, self.cleaned_data['api_id']))
if int(e.code) in [221, 222]:
raise forms.ValidationError("API key failed validation")
else:
raise forms.ValidationError("Failed to reach API servers")

View File

@ -1,7 +1,13 @@
from django.contrib import admin from django.contrib import admin
from models import HRApplication from models import Application
from models import HRApplicationComment from models import ApplicationQuestion
from models import ApplicationForm
from models import ApplicationResponse
from models import ApplicationComment
admin.site.register(HRApplication) admin.site.register(Application)
admin.site.register(HRApplicationComment) admin.site.register(ApplicationComment)
admin.site.register(ApplicationQuestion)
admin.site.register(ApplicationForm)
admin.site.register(ApplicationResponse)

View File

@ -1,33 +1,7 @@
from django import forms from django import forms
from django.conf import settings
from eveonline.models import EveCorporationInfo
class HRApplicationForm(forms.Form):
allchoices = []
if settings.IS_CORP:
corp = EveCorporationInfo.objects.get(corporation_id=settings.CORP_ID)
allchoices.append((str(corp.corporation_id), str(corp.corporation_name)))
else:
for corp in EveCorporationInfo.objects.all():
if corp.alliance is not None:
if corp.alliance.alliance_id == settings.ALLIANCE_ID:
allchoices.append((str(corp.corporation_id), str(corp.corporation_name)))
character_name = forms.CharField(max_length=254, required=True, label="Main Character Name")
full_api_id = forms.CharField(max_length=254, required=True, label="API ID")
full_api_key = forms.CharField(max_length=254, required=True, label="API Verification Code")
corp = forms.ChoiceField(choices=allchoices, required=True, label="Corp")
is_a_spi = forms.ChoiceField(choices=[('Yes', 'Yes'), ('No', 'No')], required=True, label='Are you a spy?')
about = forms.CharField(widget=forms.Textarea, required=False, label="About You")
extra = forms.CharField(widget=forms.Textarea, required=False, label="Extra Application Info")
class HRApplicationCommentForm(forms.Form): class HRApplicationCommentForm(forms.Form):
app_id = forms.CharField(widget=forms.TextInput(attrs={'readonly': 'True'}))
comment = forms.CharField(widget=forms.Textarea, required=False, label="Comment", max_length=254) comment = forms.CharField(widget=forms.Textarea, required=False, label="Comment", max_length=254)
class HRApplicationSearchForm(forms.Form): class HRApplicationSearchForm(forms.Form):
search_string = forms.CharField(max_length=254, required=True, label="Search String") search_string = forms.CharField(max_length=254, required=True, label="Search String")

View File

@ -3,8 +3,91 @@ from django.contrib.auth.models import User
from eveonline.models import EveCharacter from eveonline.models import EveCharacter
from eveonline.models import EveCorporationInfo from eveonline.models import EveCorporationInfo
from eveonline.models import EveApiKeyPair
from authentication.models import AuthServicesInfo
class ApplicationQuestion(models.Model):
title = models.CharField(max_length=100)
help_text = models.CharField(max_length=254, blank=True, null=True)
def __str__(self):
return "Question: " + self.title
class ApplicationForm(models.Model):
questions = models.ManyToManyField(ApplicationQuestion)
corp = models.OneToOneField(EveCorporationInfo)
def __str__(self):
return str(self.corp)
class Application(models.Model):
form = models.ForeignKey(ApplicationForm, on_delete=models.CASCADE, related_name='applications')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='applications')
approved = models.NullBooleanField(blank=True, null=True, default=None)
reviewer = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
reviewer_character = models.ForeignKey(EveCharacter, on_delete=models.SET_NULL, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user) + " Application To " + str(self.form)
class Meta:
permissions = (('approve_application', 'Can approve applications'), ('reject_application', 'Can reject applications'), ('view_apis', 'Can view applicant APIs'),)
unique_together = ('form', 'user')
@property
def main_character(self):
try:
auth = AuthServicesInfo.objects.get(user=self.user)
char = EveCharacter.objects.get(character_id=auth.main_char_id)
return char
except:
return None
@property
def characters(self):
return EveCharacter.objects.filter(user=self.user)
@property
def apis(self):
return EveApiKeyPair.objects.filter(user=self.user)
@property
def reviewer_str(self):
if self.reviewer_character:
return str(self.reviewer_character)
elif self.reviewer:
return "User " + str(self.reviewer)
else:
return None
class ApplicationResponse(models.Model):
question = models.ForeignKey(ApplicationQuestion, on_delete=models.CASCADE)
application = models.ForeignKey(Application, on_delete=models.CASCADE, related_name='responses')
answer = models.TextField()
def __str__(self):
return str(self.application) + " Answer To " + str(self.question)
class Meta:
unique_together = ('question', 'application')
class ApplicationComment(models.Model):
application = models.ForeignKey(Application, on_delete=models.CASCADE, related_name='comments')
user = models.ForeignKey(User, on_delete=models.CASCADE)
text = models.TextField()
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.user) + " comment on " + str(self.application)
################
# Legacy Models
################
# Can't delete or evolutions explodes.
# They do nothing.
################
class HRApplication(models.Model): class HRApplication(models.Model):
character_name = models.CharField(max_length=254, default="") character_name = models.CharField(max_length=254, default="")
full_api_id = models.CharField(max_length=254, default="") full_api_id = models.CharField(max_length=254, default="")

View File

@ -1,12 +1,17 @@
from django.template import RequestContext from django.template import RequestContext
from django.shortcuts import render_to_response from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import HttpResponseRedirect from django.shortcuts import HttpResponseRedirect
from notifications import notify from notifications import notify
from models import HRApplication from models import HRApplication
from models import HRApplicationComment from models import HRApplicationComment
from forms import HRApplicationForm from models import ApplicationForm
from models import Application
from models import ApplicationQuestion
from models import ApplicationResponse
from models import ApplicationComment
from forms import HRApplicationCommentForm from forms import HRApplicationCommentForm
from forms import HRApplicationSearchForm from forms import HRApplicationSearchForm
from eveonline.models import EveCorporationInfo from eveonline.models import EveCorporationInfo
@ -20,195 +25,177 @@ import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def create_application_test(user):
auth, c = AuthServicesInfo.objects.get_or_create(user=user)
if auth.main_char_id:
return True
else:
return False
@login_required @login_required
def hr_application_management_view(request): def hr_application_management_view(request):
logger.debug("hr_application_management_view called by user %s" % request.user) logger.debug("hr_application_management_view called by user %s" % request.user)
personal_app = None corp_applications = []
corp_applications = None finished_corp_applications = []
auth_info, c = AuthServicesInfo.objects.get_or_create(user=request.user)
main_char = None
if auth_info.main_char_id:
try:
main_char = EveCharacter.objects.get(character_id=auth_info.main_char_id)
except:
pass
if request.user.is_superuser: if request.user.is_superuser:
logger.debug("User %s is superuser: returning all applications." % request.user) corp_applications = Application.objects.filter(approved=None)
corp_applications = HRApplication.objects.all() elif request.user.has_perm('auth.human_resources') and main_char:
else: if ApplicationForm.objects.filter(corp__corporation_id=main_char.corporation_id).exists():
# Get the corp the member is in app_form = ApplicationForm.objects.get(corp__corporation_id=main_char.corporation_id)
auth_info = AuthServicesInfo.objects.get(user=request.user) corp_applications = Application.objects.filter(form=app_form).filter(approved=None)
if auth_info.main_char_id != "": finished_corp_applications = Application.objects.filter(form=app_form).filter(approved__in=[True, False])
try: logger.debug("Retrieved %s personal, %s corp applications for %s" % (len(request.user.applications.all()), len(corp_applications), request.user))
main_corp_id = EveManager.get_charater_corporation_id_by_id(auth_info.main_char_id) context = {
main_alliance_id = EveManager.get_charater_alliance_id_by_id(auth_info.main_char_id) 'personal_apps': request.user.applications.all(),
if (settings.IS_CORP and main_corp_id == settings.CORP_ID) or (not settings.IS_CORP and main_alliance_id == settings.ALLIANCE_ID): 'applications': corp_applications,
main_char = EveCharacter.objects.get(character_id=auth_info.main_char_id) 'finished_applications': finished_corp_applications,
if EveCorporationInfo.objects.filter(corporation_id=main_char.corporation_id).exists(): 'search_form': HRApplicationSearchForm(),
corp = EveCorporationInfo.objects.get(corporation_id=main_char.corporation_id) 'create': create_application_test(request.user)
corp_applications = HRApplication.objects.filter(corp=corp).filter(approved_denied=None) }
else: return render_to_response('registered/hrapplicationmanagement.html', context, context_instance=RequestContext(request))
corp_applications = None
else:
corp_applications = None
except:
logger.error("Unable to determine user %s main character id %s corp. Returning no corp hrapplications." % (request.user, auth_info.main_char_id))
corp_applications = None
context = {'personal_apps': HRApplication.objects.all().filter(user=request.user),
'applications': corp_applications,
'search_form': HRApplicationSearchForm()}
return render_to_response('registered/hrapplicationmanagement.html',
context, context_instance=RequestContext(request))
@login_required @login_required
def hr_application_create_view(request): @user_passes_test(create_application_test)
logger.debug("hr_application_create_view called by user %s" % request.user) def hr_application_create_view(request, form_id=None):
success = False if form_id:
app_form = get_object_or_404(ApplicationForm, id=form_id)
if request.method == 'POST': if request.method == "POST":
form = HRApplicationForm(request.POST) if Application.objects.filter(user=request.user).filter(form=app_form).exists():
logger.debug("Request type POST with form valid: %s" % form.is_valid()) logger.warn("User %s attempting to duplicate application to %s" % (request.user, app_form.corp))
if form.is_valid(): else:
application = HRApplication() application = Application(user=request.user, form=app_form)
application.user = request.user application.save()
application.character_name = form.cleaned_data['character_name'] for question in app_form.questions.all():
application.full_api_id = form.cleaned_data['full_api_id'] response = ApplicationResponse(question=question, application=application)
application.full_api_key = form.cleaned_data['full_api_key'] response.answer = request.POST.get(str(question.pk), "Failed to retrieve answer provided by applicant.")
application.corp = EveCorporationInfo.objects.get(corporation_id=form.cleaned_data['corp']) response.save()
application.is_a_spi = form.cleaned_data['is_a_spi'] logger.info("%s created %s" % (request.user, application))
application.about = form.cleaned_data['about'] return redirect('auth_hrapplications_view')
application.extra = form.cleaned_data['extra'] else:
application.save() questions = app_form.questions.all()
success = True return render_to_response('registered/hrapplicationcreate.html', {'questions':questions, 'corp':app_form.corp}, context_instance=RequestContext(request))
logger.info("Created HRApplication for user %s to corp %s" % (request.user, application.corp))
else: else:
logger.debug("Providing empty form.") choices = []
form = HRApplicationForm() for app_form in ApplicationForm.objects.all():
if not Application.objects.filter(user=request.user).filter(form=app_form).exists():
context = {'form': form, 'success': success} choices.append((app_form.id, app_form.corp.corporation_name))
return render_to_response('registered/hrcreateapplication.html', return render_to_response('registered/hrapplicationcorpchoice.html', {'choices':choices}, context_instance=RequestContext(request))
context, context_instance=RequestContext(request))
@login_required @login_required
def hr_application_personal_view(request, app_id): def hr_application_personal_view(request, app_id):
logger.debug("hr_application_personal_view called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_personal_view called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
application = HRApplication.objects.get(id=app_id) if app.user == request.user:
logger.debug("Got application id %s: %s" % (app_id, application)) context = {
if application.user != request.user: 'app': app,
logger.warn("HRApplication id %s user %s does not match request user %s - returning blank application." % (app_id, application.user, request.user)) 'responses': ApplicationResponse.objects.filter(application=app),
application = HRApplication() 'buttons': False,
'comments': ApplicationComment.objects.filter(application=app),
'comment_form': HRApplicationCommentForm(),
'apis': [],
}
return render_to_response('registered/hrapplicationview.html', context, context_instance=RequestContext(request))
else: else:
logger.error("Unable to locate HRApplication matching id %s - returning blank application to user %s" % (app_id, request.user)) logger.warn("User %s not authorized to view %s" % (request.user, app))
application = HRApplication() return redirect('auth_hrapplications_view')
context = {'application': application}
return render_to_response('registered/hrapplicationview.html',
context, context_instance=RequestContext(request))
@login_required @login_required
def hr_application_personal_removal(request, app_id): def hr_application_personal_removal(request, app_id):
logger.debug("hr_application_personal_removal called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_personal_removal called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
application = HRApplication.objects.get(id=app_id) if app.user == request.user:
if application.user == request.user: if app.accepted == None:
application.delete() logger.info("User %s deleting %s" % (request.user, app))
logger.info("Deleted HRApplication with id %s for user %s to corp %s" % (app_id, request.user, application.corp)) app.delete()
else: else:
logger.error("HRapplication id %s user %s does not match request user %s - refusing to delete." % (app_id, application.user, request.user)) logger.warn("User %s attempting to delete reviewed app %s" % (request.user, app))
return HttpResponseRedirect("/hr_application_management/") else:
logger.warn("User %s not authorized to delete %s" % (request.user, app))
return redirect('auth_hrapplications_view')
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
def hr_application_view(request, app_id): def hr_application_view(request, app_id):
logger.debug("hr_application_view called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_view called by user %s for app id %s" % (request.user, app_id))
app = get_object_or_404(Application, pk=app_id)
if request.method == 'POST': if request.method == 'POST':
form = HRApplicationCommentForm(request.POST) if request.user.has_perm('hrapplications.add_applicationcomment'):
logger.debug("Request type POST contains form valid: %s" % form.is_valid()) form = HRApplicationCommentForm(request.POST)
if form.is_valid(): logger.debug("Request type POST contains form valid: %s" % form.is_valid())
auth_info = AuthServicesInfo.objects.get(user=request.user) if form.is_valid():
comment = ApplicationComment()
comment = HRApplicationComment() comment.application = app
comment.application = HRApplication.objects.get(id=int(form.cleaned_data['app_id'])) comment.user = request.user
comment.commenter_user = request.user comment.text = form.cleaned_data['comment']
comment.commenter_character = EveCharacter.objects.get(character_id=auth_info.main_char_id) comment.save()
comment.comment = form.cleaned_data['comment'] logger.info("Saved comment by user %s to %s" % (request.user, app))
comment.save() else:
logger.info("Saved comment by user %s to hrapplication %s" % (request.user, comment.application)) logger.warn("User %s does not have permission to add ApplicationComments" % request.user)
else: else:
logger.debug("Returning blank HRApplication comment form.") logger.debug("Returning blank HRApplication comment form.")
form = HRApplicationCommentForm() form = HRApplicationCommentForm()
apis = []
if HRApplication.objects.filter(id=app_id).exists(): if request.user.has_perm('hrapplications.view_apis'):
application = HRApplication.objects.get(id=app_id) apis = app.apis
comments = HRApplicationComment.objects.all().filter(application=application) context = {
logger.debug("Retrieved hrpplication id %s on behalf of user %s with comments %s" % (app_id, request.user, len(comments))) 'app': app,
else: 'responses': ApplicationResponse.objects.filter(application=app),
application = HRApplication() 'buttons': True,
comments = [] 'apis': apis,
logger.error("HRAppllication with id %s not found - returning blank applicatin to user %s" % request.user) 'comments': ApplicationComment.objects.filter(application=app),
'comment_form': form,
context = {'application': application, 'comments': comments, 'comment_form': form} }
return render_to_response('registered/hrapplicationview.html', context, context_instance=RequestContext(request))
return render_to_response('registered/hrapplicationview.html',
context, context_instance=RequestContext(request))
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
@permission_required('hrapplications.delete_application')
def hr_application_remove(request, app_id): def hr_application_remove(request, app_id):
logger.debug("hr_application_remove called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_remove called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
application = HRApplication.objects.get(id=app_id) logger.info("User %s deleting %s" % (request.user, app))
if application: app.delete()
logger.info("Deleted HRApplication id %s on behalf of user %s" % (app_id, request.user)) notify(app.user, "Application Deleted", message="Your application to %s was deleted." % app.form.corp)
notify(application.user, "Application Deleted", message="Your application to %s was deleted.") return redirect('auth_hrapplications_view')
application.delete()
else:
logger.error("Unable to delete HRApplication with id %s on behalf of user %s: application is NoneType" % (app_id, request.user))
else:
logger.error("Unable to delete HRApplication with id %s on behalf of user %s: application not found." % (app_id, request.user))
return HttpResponseRedirect("/hr_application_management/")
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
@permission_required('hrapplications.approve_application')
def hr_application_approve(request, app_id): def hr_application_approve(request, app_id):
logger.debug("hr_application_approve called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_approve called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
auth_info = AuthServicesInfo.objects.get(user=request.user) if request.user.is_superuser or request.user == app.reviewer:
application = HRApplication.objects.get(id=app_id) logger.info("User %s approving %s" % (request.user, app))
application.approved_denied = True app.approved = True
application.reviewer_user = request.user app.save()
application.reviewer_character = EveCharacter.objects.get(character_id=auth_info.main_char_id) notify(app.user, "Application Accepted", message="Your application to %s has been approved." % app.form.corp, level="success")
application.save()
logger.info("HRApplication for user %s to corp %s approved by %s" % (application.user, application.corp, request.user))
notify(application.user, "Application Accepted", message="Your application to %s has been approved." % application.corp, level="success")
else: else:
logger.error("User %s unable to approve HRApplication id %s - hrapplication with that id not found." % (request.user, app_id)) logger.warn("User %s not authorized to approve %s" % (request.user, app))
return redirect('auth_hrapplications_view')
return HttpResponseRedirect("/hr_application_management/")
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
@permission_required('hrapplications.reject_application')
def hr_application_reject(request, app_id): def hr_application_reject(request, app_id):
logger.debug("hr_application_reject called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_reject called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
auth_info = AuthServicesInfo.objects.get(user=request.user) if request.user.is_superuser or request.user == app.reviewer:
application = HRApplication.objects.get(id=app_id) logger.info("User %s rejecting %s" % (request.user, app))
application.approved_denied = False app.approved = False
application.reviewer_user = request.user app.save()
application.reviewer_character = EveCharacter.objects.get(character_id=auth_info.main_char_id) notify(app.user, "Application Rejected", message="Your application to %s has been rejected." % app.form.corp, level="danger")
application.save()
logger.info("HRApplication for user %s to corp %s rejected by %s" % (application.user, application.corp, request.user))
notify(application.user, "Application Rejected", message="Your application to %s has been rejected." % application.corp, level="danger")
else: else:
logger.error("User %s unable to reject HRApplication id %s - hrapplication with that id not found." % (request.user, app_id)) logger.warn("User %s not authorized to reject %s" % (request.user, app))
return redirect('auth_hrapplications_view')
return HttpResponseRedirect("/hr_application_management/")
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
@ -218,16 +205,37 @@ def hr_application_search(request):
form = HRApplicationSearchForm(request.POST) form = HRApplicationSearchForm(request.POST)
logger.debug("Request type POST contains form valid: %s" % form.is_valid()) logger.debug("Request type POST contains form valid: %s" % form.is_valid())
if form.is_valid(): if form.is_valid():
# Really dumb search and only checks character name searchstring = form.cleaned_data['search_string'].lower()
# This can be improved but it does the job for now applications = set([])
searchstring = form.cleaned_data['search_string']
applications = []
logger.debug("Searching for application with character name %s for user %s" % (searchstring, request.user)) logger.debug("Searching for application with character name %s for user %s" % (searchstring, request.user))
app_list = []
for application in HRApplication.objects.all(): if request.user.is_superuser:
if searchstring in application.character_name: app_list = Application.objects.all()
applications.append(application) else:
logger.info("Found %s HRApplications for user %s matching search string %s" % (len(applications), request.user, searchstring)) auth_info = AuthServicesInfo.objects.get(user=request.user)
try:
character = EveCharacter.objects.get(character_id=auth_info.main_char_id)
app_list = Application.objects.filter(form__corp__corporation_id=character.corporation_id)
except:
logger.warn("User %s missing main character model: unable to filter applications to search" % request.user)
for application in app_list:
if application.main_character:
if searchstring in application.main_character.character_name.lower():
applications.add(application)
if searchstring in application.main_character.corporation_name.lower():
applications.add(application)
if searchstring in application.main_character.alliance_name.lower():\
applications.add(application)
for character in application.characters:
if searchstring in character.character_name.lower():
applications.add(application)
if searchstring in character.corporation_name.lower():
applications.add(application)
if searchstring in character.alliance_name.lower():
applications.add(application)
if searchstring in application.user.username.lower():
applications.add(application)
logger.info("Found %s Applications for user %s matching search string %s" % (len(applications), request.user, searchstring))
context = {'applications': applications, 'search_form': HRApplicationSearchForm()} context = {'applications': applications, 'search_form': HRApplicationSearchForm()}
@ -243,19 +251,23 @@ def hr_application_search(request):
logger.debug("Returning empty search form for user %s" % request.user) logger.debug("Returning empty search form for user %s" % request.user)
return HttpResponseRedirect("/hr_application_management/") return HttpResponseRedirect("/hr_application_management/")
@login_required @login_required
@permission_required('auth.human_resources') @permission_required('auth.human_resources')
def hr_application_mark_in_progress(request, app_id): def hr_application_mark_in_progress(request, app_id):
logger.debug("hr_application_mark_in_progress called by user %s for app id %s" % (request.user, app_id)) logger.debug("hr_application_mark_in_progress called by user %s for app id %s" % (request.user, app_id))
if HRApplication.objects.filter(id=app_id).exists(): app = get_object_or_404(Application, pk=app_id)
if not app.reviewer:
logger.info("User %s marking %s in progress" % (request.user, app))
auth_info = AuthServicesInfo.objects.get(user=request.user) auth_info = AuthServicesInfo.objects.get(user=request.user)
application = HRApplication.objects.get(id=app_id) try:
application.reviewer_inprogress_character = EveCharacter.objects.get(character_id=auth_info.main_char_id) character = EveCharacter.objects.get(character_id=auth_info.main_char_id)
application.save() except:
logger.info("Marked HRApplication for user %s to corp %s in progress by user %s" % (application.user, application.corp, request.user)) logger.warn("User %s marking %s in review has no main character" % (request.user, app))
notify(application.user, "Application In Progress", message="Your application to %s is being reviewed by %s" % (application.corp, application.reviewer_inprogress_character)) character = None
app.reviewer = request.user
app.reviewer_character = character
app.save()
notify(app.user, "Application In Progress", message="Your application to %s is being reviewed by %s" % (app.form.corp, app.reviewer_str))
else: else:
logger.error("Unable to mark HRApplication id %s in progress by user %s - hrapplication matching id not found." % (app_id, request.user)) logger.warn("User %s unable to mark %s in progress: already being reviewed by %s" % (request.user, app, app.reviewer))
return HttpResponseRedirect("/hr_application_view/" + str(app_id)) return HttpResponseRedirect("/hr_application_view/" + str(app_id))

View File

@ -5,7 +5,7 @@ evelink
dnspython dnspython
passlib passlib
requests>=2.9.1 requests>=2.9.1
#requests_cache zeroc-ice
# Django Stuff # # Django Stuff #
django==1.6.5 django==1.6.5

View File

@ -1,6 +1,7 @@
from django.contrib import admin from django.contrib import admin
from .models import AuthTS from .models import AuthTS
from .models import DiscordAuthToken from .models import DiscordAuthToken
from .models import MumbleUser
class AuthTSgroupAdmin(admin.ModelAdmin): class AuthTSgroupAdmin(admin.ModelAdmin):
fields = ['auth_group','ts_group'] fields = ['auth_group','ts_group']
@ -9,3 +10,5 @@ class AuthTSgroupAdmin(admin.ModelAdmin):
admin.site.register(AuthTS, AuthTSgroupAdmin) admin.site.register(AuthTS, AuthTSgroupAdmin)
admin.site.register(DiscordAuthToken) admin.site.register(DiscordAuthToken)
admin.site.register(MumbleUser)

View File

@ -16,14 +16,10 @@ class EveApiManager():
def get_characters_from_api(api_id, api_key): def get_characters_from_api(api_id, api_key):
chars = [] chars = []
logger.debug("Getting characters from api id %s" % api_id) logger.debug("Getting characters from api id %s" % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) # Should get characters
# Should get characters account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) chars = account.characters()
chars = account.characters()
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
logger.debug("Retrieved characters %s from api id %s" % (chars, api_id)) logger.debug("Retrieved characters %s from api id %s" % (chars, api_id))
return chars return chars
@ -31,15 +27,11 @@ class EveApiManager():
def get_corporation_ticker_from_id(corp_id): def get_corporation_ticker_from_id(corp_id):
logger.debug("Getting ticker for corp id %s" % corp_id) logger.debug("Getting ticker for corp id %s" % corp_id)
ticker = "" ticker = ""
try: api = evelink.api.API()
api = evelink.api.API() corp = evelink.corp.Corp(api)
corp = evelink.corp.Corp(api) response = corp.corporation_sheet(corp_id)
response = corp.corporation_sheet(corp_id) logger.debug("Retrieved corp sheet for id %s: %s" % (corp_id, response))
logger.debug("Retrieved corp sheet for id %s: %s" % (corp_id, response)) ticker = response[0]['ticker']
ticker = response[0]['ticker']
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
logger.debug("Determined corp id %s ticker: %s" % (corp_id, ticker)) logger.debug("Determined corp id %s ticker: %s" % (corp_id, ticker))
return ticker return ticker
@ -47,13 +39,10 @@ class EveApiManager():
def get_alliance_information(alliance_id): def get_alliance_information(alliance_id):
results = {} results = {}
logger.debug("Getting info for alliance with id %s" % alliance_id) logger.debug("Getting info for alliance with id %s" % alliance_id)
try: api = evelink.api.API()
api = evelink.api.API() eve = evelink.eve.EVE(api=api)
eve = evelink.eve.EVE(api=api) alliance = eve.alliances()
alliance = eve.alliances() results = alliance[0][int(alliance_id)]
results = alliance[0][int(alliance_id)]
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
logger.debug("Got alliance info %s" % results) logger.debug("Got alliance info %s" % results)
return results return results
@ -61,88 +50,57 @@ class EveApiManager():
def get_corporation_information(corp_id): def get_corporation_information(corp_id):
logger.debug("Getting info for corp with id %s" % corp_id) logger.debug("Getting info for corp with id %s" % corp_id)
results = {} results = {}
try: api = evelink.api.API()
api = evelink.api.API() corp = evelink.corp.Corp(api=api)
corp = evelink.corp.Corp(api=api) corpinfo = corp.corporation_sheet(corp_id=int(corp_id))
corpinfo = corp.corporation_sheet(corp_id=int(corp_id)) results = corpinfo[0]
results = corpinfo[0]
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
logger.debug("Got corp info %s" % results) logger.debug("Got corp info %s" % results)
return results return results
@staticmethod @staticmethod
def check_api_is_type_account(api_id, api_key): def check_api_is_type_account(api_id, api_key):
logger.debug("Checking if api id %s is account." % api_id) logger.debug("Checking if api id %s is account." % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) info = account.key_info()
info = account.key_info() logger.debug("API id %s is type %s" % (api_id, info[0]['type']))
logger.debug("API id %s is type %s" % (api_id, info[0]['type'])) return info[0]['type'] == "account"
return info[0]['type'] == "account"
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
return None
@staticmethod @staticmethod
def check_api_is_full(api_id, api_key): def check_api_is_full(api_id, api_key):
logger.debug("Checking if api id %s meets member requirements." % api_id) logger.debug("Checking if api id %s meets member requirements." % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) info = account.key_info()
info = account.key_info() logger.debug("API has mask %s, required is %s" % (info[0]['access_mask'], settings.MEMBER_API_MASK))
logger.debug("API has mask %s, required is %s" % (info[0]['access_mask'], settings.MEMBER_API_MASK)) return info[0]['access_mask'] & int(settings.MEMBER_API_MASK) == int(settings.MEMBER_API_MASK)
return info[0]['access_mask'] & int(settings.MEMBER_API_MASK) == int(settings.MEMBER_API_MASK)
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
return None
@staticmethod @staticmethod
def check_blue_api_is_full(api_id, api_key): def check_blue_api_is_full(api_id, api_key):
logger.debug("Checking if api id %s meets blue requirements." % api_id) logger.debug("Checking if api id %s meets blue requirements." % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) info = account.key_info()
info = account.key_info() logger.debug("API has mask %s, required is %s" % (info[0]['access_mask'], settings.BLUE_API_MASK))
logger.debug("API has mask %s, required is %s" % (info[0]['access_mask'], settings.BLUE_API_MASK)) return info[0]['access_mask'] & int(settings.BLUE_API_MASK) == int(settings.BLUE_API_MASK)
return info[0]['access_mask'] & int(settings.BLUE_API_MASK) == int(settings.BLUE_API_MASK)
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
return None
@staticmethod @staticmethod
def get_api_info(api_id, api_key): def get_api_info(api_id, api_key):
logger.debug("Getting api info for key id %s" % api_id) logger.debug("Getting api info for key id %s" % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) info = account.key_info()
info = account.key_info() logger.debug("Got info for api id %s: %s" % (api_id, info))
logger.debug("Got info for api id %s: %s" % (api_id, info)) return info
return info
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.")
return None
@staticmethod @staticmethod
def api_key_is_valid(api_id, api_key): def api_key_is_valid(api_id, api_key):
logger.debug("Checking if api id %s is valid." % api_id) logger.debug("Checking if api id %s is valid." % api_id)
try: api = evelink.api.API(api_key=(api_id, api_key))
api = evelink.api.API(api_key=(api_id, api_key)) account = evelink.account.Account(api=api)
account = evelink.account.Account(api=api) info = account.key_info()
info = account.key_info() logger.info("Verified api id %s is still valid." % api_id)
logger.info("Verified api id %s is still valid." % api_id) return True
return True
except evelink.api.APIError as error:
logger.exception("APIError occured while validating api id %s" % api_id)
logger.info("API id %s is invalid." % api_id)
return False
@staticmethod @staticmethod
def check_if_api_server_online(): def check_if_api_server_online():
@ -170,6 +128,9 @@ class EveApiManager():
logger.debug("Confirmed id %s is a corp." % corp_id) logger.debug("Confirmed id %s is a corp." % corp_id)
return True return True
except evelink.api.APIError as error: except evelink.api.APIError as error:
if int(error.code) == '523':
logger.debug("Confirmed id %s is not a corp" % corp_id)
return False
logger.debug("APIError occured while checking if id %s is corp. Possibly not corp?" % corp_id) logger.debug("APIError occured while checking if id %s is corp. Possibly not corp?" % corp_id)
logger.debug("Unable to verify id %s is corp." % corp_id) logger.debug("Unable to verify id %s is corp." % corp_id)
@ -178,16 +139,13 @@ class EveApiManager():
@staticmethod @staticmethod
def get_corp_standings(): def get_corp_standings():
if settings.CORP_API_ID and settings.CORP_API_VCODE: if settings.CORP_API_ID and settings.CORP_API_VCODE:
try: logger.debug("Getting corp standings with api id %s" % settings.CORP_API_ID)
logger.debug("Getting corp standings with api id %s" % settings.CORP_API_ID) api = evelink.api.API(api_key=(settings.CORP_API_ID, settings.CORP_API_VCODE))
api = evelink.api.API(api_key=(settings.CORP_API_ID, settings.CORP_API_VCODE)) corp = evelink.corp.Corp(api=api)
corp = evelink.corp.Corp(api=api) corpinfo = corp.contacts()
corpinfo = corp.contacts() results = corpinfo.result
results = corpinfo.result logger.debug("Got corp standings from settings: %s" % results)
logger.debug("Got corp standings from settings: %s" % results) return results
return results
except evelink.api.APIError as error:
logger.exception("Unhandled APIError occured.", exc_info=True)
else: else:
logger.error("No corp API key supplied in settings. Unable to get standings.") logger.error("No corp API key supplied in settings. Unable to get standings.")
return {} return {}

View File

@ -6,46 +6,13 @@ import django
from django.db import connections from django.db import connections
from django.conf import settings from django.conf import settings
from services.models import MumbleUser
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class MumbleManager: class MumbleManager:
SQL_SELECT_USER_MAX_ID = r"SELECT max(user_id)+1 as next_id from murmur_users"
SQL_SELECT_GROUP_MAX_ID = r"SELECT MAX(group_id)+1 FROM murmur_groups"
SQL_CREATE_USER = r"INSERT INTO murmur_users (server_id, user_id, name, pw) VALUES (%s, %s, %s, %s)"
SQL_SELECT_GET_USER_ID_BY_NAME = r"SELECT user_id from murmur_users WHERE name = %s AND server_id = %s"
SQL_CHECK_USER_EXIST = r"SELECT name from murmur_users WHERE name = %s AND server_id = %s"
SQL_DELETE_USER = r"DELETE FROM murmur_users WHERE name = %s AND server_id = %s"
SQL_UPDATE_USER_PASSWORD = r"UPDATE murmur_users SET pw = %s WHERE name = %s AND server_id = %s"
SQL_GET_GROUPS = r"SELECT group_id, name FROM murmur_groups WHERE server_id = %s AND channel_id = 0"
SQL_GET_GROUP_FROM_NAME = r"SELECT group_id, name FROM murmur_groups " \
r"WHERE server_id = %s AND channel_id = 0 AND name = %s"
SQL_GET_USER_GROUPS = r"SELECT murmur_groups.name FROM murmur_groups, murmur_group_members WHERE " \
r"murmur_group_members.group_id = murmur_groups.group_id AND " \
r"murmur_group_members.server_id = murmur_groups.server_id AND " \
r"murmur_group_members.user_id = %s"
SQL_ADD_GROUP = r"INSERT INTO murmur_groups (group_id, server_id, name, channel_id, inherit, inheritable) " \
r"VALUES (%s, %s, %s, 0, 1, 1)"
SQL_ADD_USER_TO_GROUP = r"INSERT INTO murmur_group_members (group_id, server_id, user_id, addit) " \
r"VALUES (%s, %s, %s, 1)"
SQL_DELETE_USER_FROM_GROUP = r"DELETE FROM murmur_group_members WHERE group_id = %s " \
r"AND server_id = %s AND user_id = %s"
def __init__(self):
pass
@staticmethod @staticmethod
def __santatize_username(username): def __santatize_username(username):
@ -69,192 +36,81 @@ class MumbleManager:
def _gen_pwhash(password): def _gen_pwhash(password):
return hashlib.sha1(password).hexdigest() return hashlib.sha1(password).hexdigest()
@staticmethod
def _get_groups():
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_GET_GROUPS, [settings.MUMBLE_SERVER_ID])
rows = dbcursor.fetchall()
out = {}
for row in rows:
out[row[1]] = row[0]
logger.debug("Got mumble groups %s" % out)
return out
@staticmethod
def _get_group(name):
logger.debug("Looking for group name %s in mumble." % name)
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_GET_GROUP_FROM_NAME, [settings.MUMBLE_SERVER_ID, name])
row = dbcursor.fetchone()
if row:
logger.debug("Found group %s in mumble - %s" % (name, row[0]))
return row[0]
@staticmethod
def _get_user_groups(name):
logger.debug("Getting mumble groups for username %s" % name)
dbcursor = connections['mumble'].cursor()
user_id = MumbleManager.get_user_id_by_name(name)
dbcursor.execute(MumbleManager.SQL_GET_USER_GROUPS, [user_id])
out = [row[0] for row in dbcursor.fetchall()]
logger.debug("Got user %s mumble groups %s" % (name, out))
return out
@staticmethod
def _add_group(name):
logger.debug("Adding group %s to mumble server." % name)
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_SELECT_GROUP_MAX_ID)
row = dbcursor.fetchone()
groupid = row[0]
dbcursor.execute(MumbleManager.SQL_ADD_GROUP, [groupid, settings.MUMBLE_SERVER_ID, name])
logger.info("Created group %s on mumble server with id %s" % (name, groupid))
return groupid
@staticmethod
def _add_user_to_group(userid, groupid):
if userid != None:
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_ADD_USER_TO_GROUP, [groupid, settings.MUMBLE_SERVER_ID, userid])
logger.info("Added user id %s to mumble group id %s" % (userid, groupid))
@staticmethod
def _del_user_from_group(userid, groupid):
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_DELETE_USER_FROM_GROUP, [groupid, settings.MUMBLE_SERVER_ID, userid])
logger.info("Removed user id %s from mumble group id %s" % (userid, groupid))
@staticmethod
def get_user_id_by_name(name):
logger.debug("Getting mumble user id for user with name %s" % name)
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_SELECT_GET_USER_ID_BY_NAME, [name, settings.MUMBLE_SERVER_ID])
row = dbcursor.fetchone()
if row:
logger.debug("Got mumble user id %s for name %s" % (row[0], name))
return row[0]
@staticmethod @staticmethod
def create_user(corp_ticker, username): def create_user(corp_ticker, username):
logger.debug("Creating mumble user with username %s and ticker %s" % (username, corp_ticker)) logger.debug("Creating mumble user with username %s and ticker %s" % (username, corp_ticker))
dbcursor = connections['mumble'].cursor()
username_clean = MumbleManager.__santatize_username(MumbleManager.__generate_username(username, corp_ticker)) username_clean = MumbleManager.__santatize_username(MumbleManager.__generate_username(username, corp_ticker))
password = MumbleManager.__generate_random_pass() password = MumbleManager.__generate_random_pass()
pwhash = MumbleManager._gen_pwhash(password) pwhash = MumbleManager._gen_pwhash(password)
logger.debug("Proceeding with mumble user creation: clean username %s, pwhash starts with %s" % (username_clean, pwhash[0:5])) logger.debug("Proceeding with mumble user creation: clean username %s, pwhash starts with %s" % (username_clean, pwhash[0:5]))
try: if MumbleUser.objects.filter(username=username_clean).exists() is False:
dbcursor.execute(MumbleManager.SQL_SELECT_USER_MAX_ID) logger.info("Creating mumble user %s" % username_clean)
user_id = dbcursor.fetchone()[0] model = MumbleUser.objects.create(username=username_clean, pwhash=pwhash)
return username_clean, password
dbcursor.execute(MumbleManager.SQL_CREATE_USER, else:
[settings.MUMBLE_SERVER_ID, user_id, username_clean, pwhash]) logger.warn("Mumble user %s already exists. Updating password")
logger.info("Added user to mumble with username %s" % username_clean) model = MumbleUser.objects.get(username=username_clean)
model.pwhash = pwhash
model.save()
logger.info("Updated mumble user %s" % username_clean)
return username_clean, password return username_clean, password
except django.db.utils.IntegrityError as error:
logger.exception("IntegrityError during mumble create_user occured.")
except:
logger.exception("Unhandled exception occured.")
logger.error("Exception prevented creation of mumble user. Returning blank for username, password.")
return "", ""
@staticmethod @staticmethod
def create_blue_user(corp_ticker, username): def create_blue_user(corp_ticker, username):
logger.debug("Creating mumble blue user with username %s and ticker %s" % (username, corp_ticker)) logger.debug("Creating mumble blue user with username %s and ticker %s" % (username, corp_ticker))
dbcursor = connections['mumble'].cursor() username_clean = MumbleManager.__santatize_username(MumbleManager.__generate_username_blue(username, corp_ticker))
username_clean = MumbleManager.__santatize_username(MumbleManager.__generate_username_blue(username,
corp_ticker))
password = MumbleManager.__generate_random_pass() password = MumbleManager.__generate_random_pass()
pwhash = MumbleManager._gen_pwhash(password) pwhash = MumbleManager._gen_pwhash(password)
logger.debug("Proceeding with mumble user creation: clean username %s, pwhash starts with %s" % (username_clean, pwhash[0:5])) logger.debug("Proceeding with mumble user creation: clean username %s, pwhash starts with %s" % (username_clean, pwhash[0:5]))
try: if MumbleUser.objects.filter(username=username_clean).exists() is False:
dbcursor.execute(MumbleManager.SQL_SELECT_USER_MAX_ID) logger.info("Creating mumble user %s" % username_clean)
user_id = dbcursor.fetchone()[0] model = MumbleUser.objects.create(username=username_clean, pwhash=pwhash)
return username_clean, password
dbcursor.execute(MumbleManager.SQL_CREATE_USER, else:
[settings.MUMBLE_SERVER_ID, user_id, username_clean, pwhash]) logger.warn("Mumble user %s already exists. Updating password")
logger.info("Added blue user to mumble with username %s" % username_clean) model = MumbleUser.objects.get(username=username_clean)
model.pwhash = pwhash
model.save()
logger.info("Updated mumble user %s" % username_clean)
return username_clean, password return username_clean, password
except:
logger.exception("Unhandled exception occured.")
logger.error("Exception prevented creation of mumble blue user. Returning blank for username, password.")
return "", ""
@staticmethod
def check_user_exist(username):
logger.debug("Checking if username %s exists on mumble." % username)
dbcursor = connections['mumble'].cursor()
dbcursor.execute(MumbleManager.SQL_CHECK_USER_EXIST,
[username, settings.MUMBLE_SERVER_ID])
row = dbcursor.fetchone()
if row and row[0].lower() == username.lower():
logger.debug("Found username %s on mumble." % username)
return True
logger.debug("Unable to find username %s on mumble." % username)
return False
@staticmethod @staticmethod
def delete_user(username): def delete_user(username):
logger.debug("Deleting user %s from mumble." % username) logger.debug("Deleting user %s from mumble." % username)
dbcursor = connections['mumble'].cursor() if MumbleUser.objects.filter(username=username).exists():
if MumbleManager.check_user_exist(username): MumbleUser.objects.filter(username=username).delete()
try: logger.info("Deleted user %s from mumble" % username)
return True
dbcursor.execute(MumbleManager.SQL_DELETE_USER, logger.error("Unable to delete user %s from mumble: MumbleUser model not found" % username)
[username, settings.MUMBLE_SERVER_ID])
logger.info("Deleted user %s from mumble." % username)
return True
except:
logger.exception("Exception prevented deletion of user %s from mumble." % username)
return False
logger.error("User %s not found on mumble. Unable to delete." % username)
return False return False
@staticmethod @staticmethod
def update_user_password(username, password=None): def update_user_password(username, password=None):
logger.debug("Updating mumble user %s password." % username) logger.debug("Updating mumble user %s password." % username)
dbcursor = connections['mumble'].cursor()
if not password: if not password:
password = MumbleManager.__generate_random_pass() password = MumbleManager.__generate_random_pass()
pwhash = MumbleManager._gen_pwhash(password) pwhash = MumbleManager._gen_pwhash(password)
logger.debug("Proceeding with mumble user %s password update - pwhash starts with %s" % (username, pwhash[0:5])) logger.debug("Proceeding with mumble user %s password update - pwhash starts with %s" % (username, pwhash[0:5]))
if MumbleManager.check_user_exist(username): if MumbleUser.objects.filter(username=username).exists():
try: model = MumbleUser.objects.get(username=username)
model.pwhash = pwhash
dbcursor.execute(MumbleManager.SQL_UPDATE_USER_PASSWORD, model.save()
[pwhash, username, settings.MUMBLE_SERVER_ID]) return password
logger.info("Updated mumble user %s password." % username)
return password
except:
logger.exception("Exception prevented updating of mumble user %s password." % username)
return ""
logger.error("User %s not found on mumble. Unable to update password." % username) logger.error("User %s not found on mumble. Unable to update password." % username)
return "" return ""
@staticmethod @staticmethod
def update_groups(username, groups): def update_groups(username, groups):
logger.debug("Updating mumble user %s groups %s" % (username, groups)) logger.debug("Updating mumble user %s groups %s" % (username, groups))
userid = MumbleManager.get_user_id_by_name(username) safe_groups = list(set([g.replace(' ', '-') for g in groups]))
mumble_groups = MumbleManager._get_groups() groups =''
user_groups = set(MumbleManager._get_user_groups(username)) for g in safe_groups:
act_groups = set([g.replace(' ', '-') for g in groups]) groups = groups + g + ','
addgroups = act_groups - user_groups groups = groups.strip(',')
remgroups = user_groups - act_groups if MumbleUser.objects.filter(username=username).exists():
logger.info("Updating mumble user %s groups - adding %s, removing %s" % (username, addgroups, remgroups)) logger.info("Updating mumble user %s groups to %s" % (username, safe_groups))
for g in addgroups: model = MumbleUser.objects.get(username=username)
if not g in mumble_groups: model.groups = groups
mumble_groups[g] = MumbleManager._add_group(g) model.save()
try: else:
logger.debug("Adding mumble user %s to group %s" % (userid, mumble_groups[g])) logger.error("User %s not found on mumble. Unable to update groups." % username)
MumbleManager._add_user_to_group(userid, mumble_groups[g])
except:
logger.exception("Exception occured while adding mumble user %s with id %s to group %s with id %s" % (username, userid, g, mumble_groups[g]))
for g in remgroups:
try:
logger.debug("Deleting mumble user %s from group %s" % (userid, mumble_groups[g]))
MumbleManager._del_user_from_group(userid, mumble_groups[g])
except:
logger.exception("Exception occured while removing mumble user %s with id %s from group %s with id %s" % (username, userid, g, mumble_groups[g]))

View File

@ -38,3 +38,11 @@ class DiscordAuthToken(models.Model):
def __str__(self): def __str__(self):
output = "Discord Token for email %s user %s" % (self.email, self.user) output = "Discord Token for email %s user %s" % (self.email, self.user)
return output.encode('utf-8') return output.encode('utf-8')
class MumbleUser(models.Model):
username = models.CharField(max_length=254, unique=True)
pwhash = models.CharField(max_length=40)
groups = models.TextField(blank=True, null=True)
def __str__(self):
return self.username

View File

@ -6,6 +6,7 @@ from services.models import UserTSgroup
from services.models import AuthTS from services.models import AuthTS
from services.models import TSgroup from services.models import TSgroup
from services.models import DiscordAuthToken from services.models import DiscordAuthToken
from services.models import MumbleUser
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -81,6 +82,8 @@ def disable_mumble():
auth.save() auth.save()
logger.info("Deleting all SyncGroupCache models for mumble") logger.info("Deleting all SyncGroupCache models for mumble")
SyncGroupCache.objects.filter(servicename="mumble").delete() SyncGroupCache.objects.filter(servicename="mumble").delete()
logger.info("Deleting all MumbleUser models")
MumbleUser.objects.all().delete()
def disable_ipboard(): def disable_ipboard():
if settings.ENABLE_AUTH_IPBOARD: if settings.ENABLE_AUTH_IPBOARD:

View File

@ -16,7 +16,6 @@ from managers.teamspeak3_manager import Teamspeak3Manager
from managers.discord_manager import DiscordManager from managers.discord_manager import DiscordManager
from authentication.managers import AuthServicesInfoManager from authentication.managers import AuthServicesInfoManager
from eveonline.managers import EveManager from eveonline.managers import EveManager
from celerytask.tasks import remove_all_syncgroups_for_service
from celerytask.tasks import update_jabber_groups from celerytask.tasks import update_jabber_groups
from celerytask.tasks import update_mumble_groups from celerytask.tasks import update_mumble_groups
from celerytask.tasks import update_forum_groups from celerytask.tasks import update_forum_groups
@ -135,7 +134,7 @@ def activate_forum(request):
if result[0] != "": if result[0] != "":
AuthServicesInfoManager.update_user_forum_info(result[0], result[1], request.user) AuthServicesInfoManager.update_user_forum_info(result[0], result[1], request.user)
logger.debug("Updated authserviceinfo for user %s with forum credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with forum credentials. Updating groups." % request.user)
update_forum_groups(request.user) update_forum_groups.delay(request.user)
logger.info("Succesfully activated forum for user %s" % request.user) logger.info("Succesfully activated forum for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccesful attempt to activate forum for user %s" % request.user) logger.error("Unsuccesful attempt to activate forum for user %s" % request.user)
@ -148,7 +147,6 @@ def deactivate_forum(request):
logger.debug("deactivate_forum called by user %s" % request.user) logger.debug("deactivate_forum called by user %s" % request.user)
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = Phpbb3Manager.disable_user(authinfo.forum_username) result = Phpbb3Manager.disable_user(authinfo.forum_username)
remove_all_syncgroups_for_service(request.user, "phpbb")
# false we failed # false we failed
if result: if result:
AuthServicesInfoManager.update_user_forum_info("", "", request.user) AuthServicesInfoManager.update_user_forum_info("", "", request.user)
@ -185,7 +183,7 @@ def activate_ipboard_forum(request):
if result[0] != "": if result[0] != "":
AuthServicesInfoManager.update_user_ipboard_info(result[0], result[1], request.user) AuthServicesInfoManager.update_user_ipboard_info(result[0], result[1], request.user)
logger.debug("Updated authserviceinfo for user %s with ipboard credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with ipboard credentials. Updating groups." % request.user)
update_ipboard_groups(request.user) update_ipboard_groups.delay(request.user)
logger.info("Succesfully activated ipboard for user %s" % request.user) logger.info("Succesfully activated ipboard for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccesful attempt to activate ipboard for user %s" % request.user) logger.error("Unsuccesful attempt to activate ipboard for user %s" % request.user)
@ -198,7 +196,6 @@ def deactivate_ipboard_forum(request):
logger.debug("deactivate_ipboard_forum called by user %s" % request.user) logger.debug("deactivate_ipboard_forum called by user %s" % request.user)
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = IPBoardManager.disable_user(authinfo.ipboard_username) result = IPBoardManager.disable_user(authinfo.ipboard_username)
remove_all_syncgroups_for_service(request.user, "ipboard")
# false we failed # false we failed
if result: if result:
AuthServicesInfoManager.update_user_ipboard_info("", "", request.user) AuthServicesInfoManager.update_user_ipboard_info("", "", request.user)
@ -234,7 +231,7 @@ def activate_jabber(request):
if info[0] is not "": if info[0] is not "":
AuthServicesInfoManager.update_user_jabber_info(info[0], info[1], request.user) AuthServicesInfoManager.update_user_jabber_info(info[0], info[1], request.user)
logger.debug("Updated authserviceinfo for user %s with jabber credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with jabber credentials. Updating groups." % request.user)
update_jabber_groups(request.user) update_jabber_groups.delay(request.user)
logger.info("Succesfully activated jabber for user %s" % request.user) logger.info("Succesfully activated jabber for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccesful attempt to activate jabber for user %s" % request.user) logger.error("Unsuccesful attempt to activate jabber for user %s" % request.user)
@ -247,7 +244,6 @@ def deactivate_jabber(request):
logger.debug("deactivate_jabber called by user %s" % request.user) logger.debug("deactivate_jabber called by user %s" % request.user)
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = OpenfireManager.delete_user(authinfo.jabber_username) result = OpenfireManager.delete_user(authinfo.jabber_username)
remove_all_syncgroups_for_service(request.user, "openfire")
# If our username is blank means we failed # If our username is blank means we failed
if result: if result:
AuthServicesInfoManager.update_user_jabber_info("", "", request.user) AuthServicesInfoManager.update_user_jabber_info("", "", request.user)
@ -288,7 +284,7 @@ def activate_mumble(request):
if result[0] is not "": if result[0] is not "":
AuthServicesInfoManager.update_user_mumble_info(result[0], result[1], request.user) AuthServicesInfoManager.update_user_mumble_info(result[0], result[1], request.user)
logger.debug("Updated authserviceinfo for user %s with mumble credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with mumble credentials. Updating groups." % request.user)
update_mumble_groups(request.user) update_mumble_groups.delay(request.user)
logger.info("Succesfully activated mumble for user %s" % request.user) logger.info("Succesfully activated mumble for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccessful attempt to activate mumble for user %s" % request.user) logger.error("Unsuccessful attempt to activate mumble for user %s" % request.user)
@ -301,7 +297,6 @@ def deactivate_mumble(request):
logger.debug("deactivate_mumble called by user %s" % request.user) logger.debug("deactivate_mumble called by user %s" % request.user)
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = MumbleManager.delete_user(authinfo.mumble_username) result = MumbleManager.delete_user(authinfo.mumble_username)
remove_all_syncgroups_for_service(request.user, "mumble")
# if false we failed # if false we failed
if result: if result:
AuthServicesInfoManager.update_user_mumble_info("", "", request.user) AuthServicesInfoManager.update_user_mumble_info("", "", request.user)
@ -344,7 +339,7 @@ def activate_teamspeak3(request):
if result[0] is not "": if result[0] is not "":
AuthServicesInfoManager.update_user_teamspeak3_info(result[0], result[1], request.user) AuthServicesInfoManager.update_user_teamspeak3_info(result[0], result[1], request.user)
logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user)
update_teamspeak3_groups(request.user) update_teamspeak3_groups.delay(request.user)
logger.info("Succesfully activated TS3 for user %s" % request.user) logger.info("Succesfully activated TS3 for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccessful attempt to activate TS3 for user %s" % request.user) logger.error("Unsuccessful attempt to activate TS3 for user %s" % request.user)
@ -358,8 +353,6 @@ def deactivate_teamspeak3(request):
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid) result = Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid)
remove_all_syncgroups_for_service(request.user, "teamspeak3")
# if false we failed # if false we failed
if result: if result:
AuthServicesInfoManager.update_user_teamspeak3_info("", "", request.user) AuthServicesInfoManager.update_user_teamspeak3_info("", "", request.user)
@ -378,8 +371,6 @@ def reset_teamspeak3_perm(request):
logger.debug("Deleting TS3 user for user %s" % request.user) logger.debug("Deleting TS3 user for user %s" % request.user)
Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid) Teamspeak3Manager.delete_user(authinfo.teamspeak3_uid)
remove_all_syncgroups_for_service(request.user, "teamspeak3")
if check_if_user_has_permission(request.user, "blue_member"): if check_if_user_has_permission(request.user, "blue_member"):
logger.debug("Generating new permission key for blue user %s with main character %s" % (request.user, character)) logger.debug("Generating new permission key for blue user %s with main character %s" % (request.user, character))
result = Teamspeak3Manager.generate_new_blue_permissionkey(authinfo.teamspeak3_uid, character.character_name, result = Teamspeak3Manager.generate_new_blue_permissionkey(authinfo.teamspeak3_uid, character.character_name,
@ -393,7 +384,7 @@ def reset_teamspeak3_perm(request):
if result != "": if result != "":
AuthServicesInfoManager.update_user_teamspeak3_info(result[0], result[1], request.user) AuthServicesInfoManager.update_user_teamspeak3_info(result[0], result[1], request.user)
logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user) logger.debug("Updated authserviceinfo for user %s with TS3 credentials. Updating groups." % request.user)
update_teamspeak3_groups(request.user) update_teamspeak3_groups.delay(request.user)
logger.info("Successfully reset TS3 permission key for user %s" % request.user) logger.info("Successfully reset TS3 permission key for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
logger.error("Unsuccessful attempt to reset TS3 permission key for user %s" % request.user) logger.error("Unsuccessful attempt to reset TS3 permission key for user %s" % request.user)
@ -413,7 +404,6 @@ def deactivate_discord(request):
authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) authinfo = AuthServicesInfoManager.get_auth_service_info(request.user)
result = DiscordManager.delete_user(authinfo.discord_uid) result = DiscordManager.delete_user(authinfo.discord_uid)
if result: if result:
remove_all_syncgroups_for_service(request.user, "discord")
AuthServicesInfoManager.update_user_discord_info("", request.user) AuthServicesInfoManager.update_user_discord_info("", request.user)
logger.info("Succesfully deactivated discord for user %s" % request.user) logger.info("Succesfully deactivated discord for user %s" % request.user)
return HttpResponseRedirect("/services/") return HttpResponseRedirect("/services/")
@ -453,7 +443,7 @@ def activate_discord(request):
if user_id != "": if user_id != "":
AuthServicesInfoManager.update_user_discord_info(user_id, request.user) AuthServicesInfoManager.update_user_discord_info(user_id, request.user)
logger.debug("Updated discord id %s for user %s" % (user_id, request.user)) logger.debug("Updated discord id %s for user %s" % (user_id, request.user))
update_discord_groups(request.user) update_discord_groups.delay(request.user)
logger.debug("Updated discord groups for user %s." % request.user) logger.debug("Updated discord groups for user %s." % request.user)
success = True success = True
logger.info("Succesfully activated discord for user %s" % request.user) logger.info("Succesfully activated discord for user %s" % request.user)
@ -592,4 +582,3 @@ def set_ipboard_password(request):
logger.debug("Rendering form for user %s" % request.user) logger.debug("Rendering form for user %s" % request.user)
context = {'form': form, 'service': 'IPBoard', 'error': error} context = {'form': form, 'service': 'IPBoard', 'error': error}
return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request)) return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request))

View File

@ -0,0 +1,25 @@
{% extends "public/base.html" %}
{% load staticfiles %}
{% block title %}Choose a Corp{% endblock %}
{% block page_title %}Choose a Corp{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">Choose a Corp</h1>
{% if choices %}
<div class="panel panel-primary">
<div class="panel-heading">Available Corps</div>
<table class="table table-responsive">
{% for choice in choices %}
<tr>
<td class="text-center">
<a href="{% url 'auth_hrapplication_create_view' choice.0 %}" class="btn btn-primary" title="Apply">{{ choice.1 }}</a>
</td>
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div class="alert alert-danger">No corps are accepting applications at this time.</div>
{% endif %}
</div>
{% endblock content %}

View File

@ -0,0 +1,31 @@
{% extends "public/base.html" %}
{% load staticfiles %}
{% block title %}Apply To {{ corp.corporation_name }}{% endblock title %}
{% block page_title %}Apply To {{ corp.corporation_name }}{% endblock page_title %}
{% block content %}
<div class="col-lg-12">
<h1 class="page-header text-center">Apply To {{ corp.corporation_name }}</h1>
<div class="container-fluid">
<div class="col-md-4 col-md-offset-4">
<div class="row">
<form class="form-signin">
{% csrf_token %}
{% for question in questions %}
<div class="form-group">
<label class="control-label" for="id_{{ question.pk }}">{{ question.title }}</label>
<div class=" ">
{% if question.help_text %}
<div cass="text-center">{{ question.help_text }}</div>
{% endif %}
<textarea class="form-control" cols="40" id="id_{{ question.pk }}" name="{{ question.pk }}" rows="10"></textarea>
</div>
</div>
{% endfor %}
<button class="btn btn-lg btn-primary btn-block" type="submit" formmethod="post">Submit</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -12,19 +12,16 @@
{% if not perms.auth.member %} {% if not perms.auth.member %}
<h1 class="page-header text-center">Personal Applications <h1 class="page-header text-center">Personal Applications
<div class="text-right"> <div class="text-right">
<a href="{% url 'auth_hrapplication_create_view' %}"> {% if create %}
{% if personal_app %} <a href="{% url 'auth_hrapplication_create_view' %}"><button type="button" class="btn btn-success">Create Application</button></a>
<button type="button" class="btn btn-success" disabled>Create Application</button> {% else %}
{% else %} <button type="button" class="btn btn-success" disabled>Create Application</button>
<button type="button" class="btn btn-success">Create Application</button> {% endif %}
{% endif %}
</a>
</div> </div>
</h1> </h1>
<table class="table table-bordered table-condensed"> <table class="table table-bordered table-condensed">
<tr> <tr>
<th class="text-center">Username</th> <th class="text-center">Username</th>
<th class="text-center">Main Character</th>
<th class="text-center">Corporation <th class="text-center">Corporation
<th class="text-center">Status</th> <th class="text-center">Status</th>
<th class="text-center">Actions</th> <th class="text-center">Actions</th>
@ -32,15 +29,14 @@
{% for personal_app in personal_apps %} {% for personal_app in personal_apps %}
<tr> <tr>
<td class="text-center">{{ personal_app.user.username }}</td> <td class="text-center">{{ personal_app.user.username }}</td>
<td class="text-center">{{ personal_app.character_name }}</td> <td class="text-center">{{ personal_app.form.corp.corporation_name }}</td>
<td class="text-center">{{ personal_app.corp.corporation_name }}</td>
<td class="text-center"> <td class="text-center">
{% if personal_app.approved_denied == None %} {% if personal_app.approved == None %}
<div class="panel panel-warning" role="alert">Pending</div> <div class="label label-warning">Pending</div>
{% elif personal_app.approved_denied == True %} {% elif personal_app.approved == True %}
<div class="panel panel-success" role="alert">Approved</div> <div class="label label-success">Approved</div>
{% else %} {% else %}
<div class="panel panel-danger" role="alert">Rejected</div> <div class="label label-danger">Rejected</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
@ -69,40 +65,93 @@
</button> </button>
</div> </div>
</h1> </h1>
<div class="container-fluid"> <ul class="nav nav-tabs">
<table class="table table-bordered"> <li class="active"><a data-toggle="tab" href="#pending">Pending</a></li>
<tr> <li><a data-toggle="tab" href="#reviewed">Reviewed</a></li>
<th class="text-center">Application ID</th> </ul>
<th class="text-center">Username</th> <div class="tab-content">
<th class="text-center">Main Character</th> <div id="pending" class="tab-pane fade in active">
<th class="text-center">Corporation</th> <div class="panel-body">
<th class="text-center">Status</th> <table class="table">
<th class="text-center">Actions</th> <tr>
</tr> <th class="text-center">Date</th>
{% for app in applications %} <th class="text-center">Username</th>
<tr> <th class="text-center">Main Character</th>
<td class="text-center">{{ app.id }}</td> <th class="text-center">Corporation</th>
<td class="text-center">{{ app.user.username }}</td> <th class="text-center">Status</th>
<td class="text-center">{{ app.character_name }}</td> <th class="text-center">Actions</th>
<td class="text-center">{{ app.corp.corporation_name }}</td> </tr>
<td class="text-center"> {% for app in applications %}
{% if app.approved_denied == None %} <tr>
<div class="panel panel-warning" role="alert">Pending</div> <td class="text-center">{{ app.created }}</td>
{% elif app.approved_denied == True %} <td class="text-center">{{ app.user.username }}</td>
<div class="panel panel-success" role="alert">Approved</div> <td class="text-center">{{ app.main_character }}</td>
{% else %} <td class="text-center">{{ app.form.corp.corporation_name }}</td>
<div class="panel panel-danger" role="alert">Rejected</div> <td class="text-center">
{% endif %} {% if app.approved_denied == None %}
</td> {% if app.reviewer_str %}
<td class="text-center"> <div class="label label-info">Reviewer: {{ app.reviewer_str }}</div>
<a href="/hr_application_view/{{ app.id }}"> {% else %}
<button type="button" class="btn btn-primary"><span <div class="label label-warning">Pending</div>
class="glyphicon glyphicon-eye-open"></span></button> {% endif %}
</a> {% elif app.approved_denied == True %}
</td> <div class="label label-success">Approved</div>
</tr> {% else %}
{% endfor %} <div class="label label-danger">Rejected</div>
</table> {% endif %}
</td>
<td class="text-center">
<a href="/hr_application_view/{{ app.id }}">
<button type="button" class="btn btn-primary"><span
class="glyphicon glyphicon-eye-open"></span></button>
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div id="reviewed" class="tab-pane fade">
<div class="panel-body">
<table class="table">
<tr>
<th class="text-center">Date</th>
<th class="text-center">Username</th>
<th class="text-center">Main Character</th>
<th class="text-center">Corporation</th>
<th class="text-center">Status</th>
<th class="text-center">Actions</th>
</tr>
{% for app in finished_applications %}
<tr>
<td class="text-center">{{ app.created }}</td>
<td class="text-center">{{ app.user.username }}</td>
<td class="text-center">{{ app.main_character }}</td>
<td class="text-center">{{ app.form.corp.corporation_name }}</td>
<td class="text-center">
{% if app.approved_denied == None %}
{% if app.reviewer_str %}
<div class="label label-info">Reviewer: {{ app.reviewer_str }}</div>
{% else %}
<div class="label label-warning">Pending</div>
{% endif %}
{% elif app.approved_denied == True %}
<div class="label label-success">Approved</div>
{% else %}
<div class="label label-danger">Rejected</div>
{% endif %}
</td>
<td class="text-center">
<a href="/hr_application_view/{{ app.id }}">
<button type="button" class="btn btn-primary"><span
class="glyphicon glyphicon-eye-open"></span></button>
</a>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -31,16 +31,16 @@
{% for app in applications %} {% for app in applications %}
<tr> <tr>
<td class="text-center">{{ app.id }}</td> <td class="text-center">{{ app.id }}</td>
<td class="text-center">{{ app.user.username }}</td> <td class="text-center">{{ app.user }}</td>
<td class="text-center">{{ app.character_name }}</td> <td class="text-center">{{ app.main_character }}</td>
<td class="text-center">{{ app.corp.corporation_name }}</td> <td class="text-center">{{ app.form.corp }}</td>
<td class="text-center"> <td class="text-center">
{% if app.approved_denied == None %} {% if app.approved == None %}
<div class="panel panel-warning" role="alert">Pending</div> <div class="label label-warning">Pending</div>
{% elif app.approved_denied == True %} {% elif app.approved == True %}
<div class="panel panel-success" role="alert">Approved</div> <div class="label label-success">Approved</div>
{% else %} {% else %}
<div class="panel panel-danger" role="alert">Rejected</div> <div class="label label-danger">Rejected</div>
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">

336
stock/templates/registered/hrapplicationview.html Executable file → Normal file
View File

@ -1,223 +1,153 @@
{% extends "public/base.html" %} {% extends "public/base.html" %}
{% load bootstrap %}
{% load staticfiles %} {% load staticfiles %}
{% load bootstrap %}
{% block title %}Alliance Auth - View Application{% endblock %} {% block title %}Alliance Auth - View Application{% endblock %}
{% block page_title %}View Application{% endblock page_title %} {% block page_title %}View Application{% endblock page_title %}
{% block extra_css %}{% endblock extra_css %} {% block extra_css %}{% endblock extra_css %}
{% block content %} {% block content %}
<div class="col-lg-12"> <div class="col-lg-12">
<h1 class="page-header text-center">View Application</h1> <h1 class="page-header text-center">View Application</h1>
<div class="container-fluid"> <div class="container-fluid">
<div class="col-md-4 col-md-offset-4"> <div class="col-md-4 col-md-offset-4">
<div class="row"> <div class="row">
<form class="form-signin"> {% if app.approved %}
<div class="form-group"> <div class="alert alert-success">Approved</div>
<label class="control-label " for="id_extra">Status</label> {% elif app.approved == False %}
<div class="alert alert-danger">Denied</div>
<div class=" "> {% else %}
{% if application.approved_denied == None %} <div class="alert alert-warning">Pending</div>
<div class="alert alert-warning" role="alert">Pending</div> {% endif %}
{% elif application.approved_denied %} {% if app.reviewer_str %}
<div class="alert alert-success" role="alert">Approved</div> <div class="alert alert-info">Reviewer: {{ app.reviewer_str }}</div>
{% else %} {% endif %}
<div class="alert alert-danger" role="alert">Rejected</div>
{% endif %}
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_extra">Reviewer</label>
<div class=" ">
{% if application.reviewer_inprogress_character == None %}
<div class="alert alert-info" role="alert">pending</div>
{% else %}
<div class="alert alert-info"
role="alert">{{ application.reviewer_inprogress_character.character_name }}</div>
{% endif %}
</div>
</div>
<div class="form-group">
<label class="control-label" for="id_character_name">User Account</label>
<div class=" ">
<input class=" form-control" value="{{ application.user.username }}" disabled>
</div>
</div>
<div class="form-group">
<label class="control-label" for="id_character_name">Main Character Name</label>
<div class=" ">
<input class=" form-control" value="{{ application.character_name }}" disabled>
</div>
</div>
<div class="form-group">
<label class="control-label" for="id_full_api_id">API ID</label>
<div class=" ">
<input class=" form-control" value="{{ application.full_api_id }}" disabled>
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_full_api_key">API Verification Code</label>
<div class=" ">
<input class=" form-control" value="{{ application.full_api_key }}" disabled>
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_corp">Corp</label>
<div class=" ">
<select class=" form-control" id="id_corp" name="corp" disabled>
<option value="98076553">{{ application.corp.corporation_name }}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_is_a_spi">Is a spy</label>
<div class=" ">
<select class=" form-control" id="id_is_a_spi" name="is_a_spi" disabled>
<option value="">{{ application.is_a_spi }}</option>
</select>
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_about">About</label>
<div class=" ">
<textarea class=" form-control" cols="40" id="id_about" name="about" rows="10"
disabled>{{ application.about }}</textarea>
</div>
</div>
<div class="form-group">
<label class="control-label " for="id_extra">Extra Application Info</label>
<div class=" ">
<textarea class=" form-control" cols="40" id="id_extra" name="extra" rows="10"
disabled>{{ application.extra }}</textarea>
</div>
</div>
{% if perms.auth.human_resources %}
<div class="form-group text-center">
<label class="control-label text-center" for="id_extra">Actions</label>
<div class="container-fluid well">
<div class="row text-center">
{% if application.reviewer_inprogress_character != None %}
<a href="/hr_application_approve/{{ application.id }}">
<button type="button" class="btn btn-lg btn-success">Approve</button>
</a>
<a href="/hr_application_reject/{{ application.id }}">
<button type="button" class="btn btn-lg btn-info">Reject</button>
</a>
<a href="/hr_application_remove/{{ application.id }}">
<button type="button" class="btn btn-lg btn-danger">Delete</button>
</a>
{% else %}
<a href="/hr_mark_in_progress/{{ application.id }}">
<button type="button" class="btn btn-lg btn-warning">Mark In Progress
</button>
</a>
{% endif %}
</div>
<br/>
<div class="row text-center">
<a href="{{ JACK_KNIFE_URL }}?usid={{ application.full_api_id }}&apik={{ application.full_api_key }}"
target="_blank">
<button type="button" class="btn btn-lg btn-primary">API Jack Knife</button>
</a>
<button type="button" class="btn btn-primary btn-lg" data-toggle="modal"
data-target="#myModal">
Comment
</button>
</div>
</div>
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#accordion"
href="#collapseThree" aria-expanded="false"
aria-controls="collapseThree">
Comments - {{ comments|length }}
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="headingThree">
<div class="panel-body">
{% for comment in comments %}
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="">
<div class="panel-title">{{comment.created_on}} - {{ comment.commenter_character.character_name }}
- {{ comment.commenter_character.corporation_name }}</div>
</div>
<div class="panel-body">{{ comment.comment }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endif %}
</form>
</div> </div>
<div class="row">
<div class="panel panel-info">
<div class="panel-heading">Applicant</div>
<table class="table">
<tr>
<th class="text-center">User</th>
<th class="text-center">Main Character</th>
</tr>
<tr>
<td class="text-center">{{ app.user }}</td>
<td class="text-center">{{ app.main_character }}</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">Characters</div>
<table class="table">
<tr>
<th class="text-center"></th>
<th class="text-center">Name</th>
<th class="text-center">Corp</th>
<th class="text-center">Alliance</th>
</tr>
{% for char in app.characters %}
<tr>
<td class="text-center">
<img class="ra-avatar img-responsive" src="https://image.eveonline.com/Character/{{ char.character_id }}_32.jpg">
</td>
<td class="text-center">{{ char.character_name }}</td>
<td class="text-center">{{ char.corporation_name }}</td>
<td class="text-center">{{ char.alliance_name }}</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="row">
{% for response in responses %}
<div class="panel panel-default">
<div class="panel-heading">{{ response.question.title }}</div>
<div class="alert">{{ response.answer }}</div>
</div>
{% endfor %}
</div>
{% if buttons %}
<div class="row">
{% if perms.auth.human_resources %}
<div class="panel panel-primary">
<div class="panel-heading">Actions</div>
{% if app.approved == None %}
{% if app.reviewer == user %}
{% if perms.hrapplications.approve_application %}
<a href="/hr_application_approve/{{ app.id }}" class="btn btn-success">Approve</a>
{% endif %}
{% if perms.hrapplications.reject_application %}
<a href="/hr_application_reject/{{ app.id }}" class="btn btn-danger">Reject</a>
{% endif %}
{% if perms.hrapplications.delete_application %}
<a href="/hr_application_remove/{{ app.id }}" class="btn btn-danger">Delete</a>
{% endif %}
{% elif not app.reviewer %}
<a href="/hr_mark_in_progress/{{ app.id }}" class="btn btn-warning">Mark in Progress</a>
{% endif %}
{% endif %}
{% if perms.hrapplications.add_applicationcomment %}
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#myModal">Comment</button>
{% endif %}
{% if perms.hrapplications.view_apis %}
{% for api in apis %}
<a href="{{ JACK_KNIFE_URL }}?usid={{ api.api_id}}&apik={{ api.api_key }}" class="btn btn-info">API {{ api.api_id }}</a>
{% endfor %}
{% endif %}
</div>
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingThree">
<h4 class="panel-title">
<a class="collapsed" data-toggle="collapse" data-parent="#accordion"
href="#collapseThree" aria-expanded="false"
aria-controls="collapseThree">
Comments - {{ comments|length }}
</a>
</h4>
</div>
<div id="collapseThree" class="panel-collapse collapse" role="tabpanel"
aria-labelledby="headingThree">
<div class="panel-body">
{% for comment in comments %}
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="">
<div class="panel-title">{{comment.created}} - {{ comment.user }}</div>
</div>
<div class="panel-body">{{ comment.text }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
</div>
{% endif %}
</div> </div>
</div> </div>
</div> </div>
{% if perms.hrapplications.add_applicationcomment %}
{% if perms.auth.human_resources %} <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" <div class="modal-dialog">
aria-hidden="true"> <div class="modal-content">
<div class="modal-dialog"> <div class="modal-header">
<div class="modal-content"> <button type="button" class="close" data-dismiss="modal">
<div class="modal-header"> <span aria-hidden="true">&times;</span><span class="sr-only">Close</span>
<button type="button" class="close" data-dismiss="modal"><span </button>
aria-hidden="true">&times;</span><span class="sr-only">Close</span></button> <h4 class="modal-title" id="myModalLabel">Add Comment</h4>
<h4 class="modal-title" id="myModalLabel">Add Comment</h4>
</div>
<div class="modal-body">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ comment_form.app_id|bootstrap }}
{{ comment_form.comment|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Add Comment</button>
</form>
</div>
<div class="modal-footer">
</div>
</div> </div>
<div class="modal-body">
<form class="form-signin" role="form" action="" method="POST">
{% csrf_token %}
{{ comment_form|bootstrap }}
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Add Comment</button>
</form>
</div>
<div class="modal-footer"></div>
</div> </div>
</div> </div>
{% endif %} </div>
{% endblock content %} {% endif %}
{% block extra_script %}
$(document).ready(function(){
var appid = {{ application.id }};
$('#id_app_id').val(appid);
$('#id_commenter_id').val(commenterid);
});
{% endblock %} {% endblock %}

View File

@ -357,7 +357,7 @@
<td class="text-center"> <td class="text-center">
{% ifequal authinfo.teamspeak3_uid "" %} {% ifequal authinfo.teamspeak3_uid "" %}
{% else %} {% else %}
<a href="ts3server://{{ TEAMSPEAK3_PUBLIC_URL }}?token={{ authinfo.teamspeak3_perm_key }}">Teamspeak3 <a href="ts3server://{{ TEAMSPEAK3_PUBLIC_URL }}?token={{ authinfo.teamspeak3_perm_key }}&nickname={{ authinfo.teamspeak3_uid }}">Teamspeak3
Link</a> Link</a>
{% endifequal %} {% endifequal %}
</td> </td>

1
thirdparty/Mumble/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
authenticator.ini

836
thirdparty/Mumble/Murmur.ice vendored Normal file
View File

@ -0,0 +1,836 @@
/**
*
* Information and control of the murmur server. Each server has
* one {@link Meta} interface that controls global information, and
* each virtual server has a {@link Server} interface.
*
**/
#include <Ice/SliceChecksumDict.ice>
module Murmur
{
/** A network address in IPv6 format.
**/
["python:seq:tuple"] sequence<byte> NetAddress;
/** A connected user.
**/
struct User {
/** Session ID. This identifies the connection to the server. */
int session;
/** User ID. -1 if the user is anonymous. */
int userid;
/** Is user muted by the server? */
bool mute;
/** Is user deafened by the server? If true, this implies mute. */
bool deaf;
/** Is the user suppressed by the server? This means the user is not muted, but does not have speech privileges in the current channel. */
bool suppress;
/** Is the user a priority speaker? */
bool prioritySpeaker;
/** Is the user self-muted? */
bool selfMute;
/** Is the user self-deafened? If true, this implies mute. */
bool selfDeaf;
/** Is the User recording? (This flag is read-only and cannot be changed using setState().) **/
bool recording;
/** Channel ID the user is in. Matches {@link Channel.id}. */
int channel;
/** The name of the user. */
string name;
/** Seconds user has been online. */
int onlinesecs;
/** Average transmission rate in bytes per second over the last few seconds. */
int bytespersec;
/** Client version. Major version in upper 16 bits, followed by 8 bits of minor version and 8 bits of patchlevel. Version 1.2.3 = 0x010203. */
int version;
/** Client release. For official releases, this equals the version. For snapshots and git compiles, this will be something else. */
string release;
/** Client OS. */
string os;
/** Client OS Version. */
string osversion;
/** Plugin Identity. This will be the user's unique ID inside the current game. */
string identity;
/** Plugin context. This is a binary blob identifying the game and team the user is on. */
string context;
/** User comment. Shown as tooltip for this user. */
string comment;
/** Client address. */
NetAddress address;
/** TCP only. True until UDP connectivity is established. */
bool tcponly;
/** Idle time. This is how many seconds it is since the user last spoke. Other activity is not counted. */
int idlesecs;
/** UDP Ping Average. This is the average ping for the user via UDP over the duration of the connection. */
float udpPing;
/** TCP Ping Average. This is the average ping for the user via TCP over the duration of the connection. */
float tcpPing;
};
sequence<int> IntList;
/** A text message between users.
**/
struct TextMessage {
/** Sessions (connected users) who were sent this message. */
IntList sessions;
/** Channels who were sent this message. */
IntList channels;
/** Trees of channels who were sent this message. */
IntList trees;
/** The contents of the message. */
string text;
};
/** A channel.
**/
struct Channel {
/** Channel ID. This is unique per channel, and the root channel is always id 0. */
int id;
/** Name of the channel. There can not be two channels with the same parent that has the same name. */
string name;
/** ID of parent channel, or -1 if this is the root channel. */
int parent;
/** List of id of linked channels. */
IntList links;
/** Description of channel. Shown as tooltip for this channel. */
string description;
/** Channel is temporary, and will be removed when the last user leaves it. */
bool temporary;
/** Position of the channel which is used in Client for sorting. */
int position;
};
/** A group. Groups are defined per channel, and can inherit members from parent channels.
**/
struct Group {
/** Group name */
string name;
/** Is this group inherited from a parent channel? Read-only. */
bool inherited;
/** Does this group inherit members from parent channels? */
bool inherit;
/** Can subchannels inherit members from this group? */
bool inheritable;
/** List of users to add to the group. */
IntList add;
/** List of inherited users to remove from the group. */
IntList remove;
/** Current members of the group, including inherited members. Read-only. */
IntList members;
};
/** Write access to channel control. Implies all other permissions (except Speak). */
const int PermissionWrite = 0x01;
/** Traverse channel. Without this, a client cannot reach subchannels, no matter which privileges he has there. */
const int PermissionTraverse = 0x02;
/** Enter channel. */
const int PermissionEnter = 0x04;
/** Speak in channel. */
const int PermissionSpeak = 0x08;
/** Whisper to channel. This is different from Speak, so you can set up different permissions. */
const int PermissionWhisper = 0x100;
/** Mute and deafen other users in this channel. */
const int PermissionMuteDeafen = 0x10;
/** Move users from channel. You need this permission in both the source and destination channel to move another user. */
const int PermissionMove = 0x20;
/** Make new channel as a subchannel of this channel. */
const int PermissionMakeChannel = 0x40;
/** Make new temporary channel as a subchannel of this channel. */
const int PermissionMakeTempChannel = 0x400;
/** Link this channel. You need this permission in both the source and destination channel to link channels, or in either channel to unlink them. */
const int PermissionLinkChannel = 0x80;
/** Send text message to channel. */
const int PermissionTextMessage = 0x200;
/** Kick user from server. Only valid on root channel. */
const int PermissionKick = 0x10000;
/** Ban user from server. Only valid on root channel. */
const int PermissionBan = 0x20000;
/** Register and unregister users. Only valid on root channel. */
const int PermissionRegister = 0x40000;
/** Register and unregister users. Only valid on root channel. */
const int PermissionRegisterSelf = 0x80000;
/** Access Control List for a channel. ACLs are defined per channel, and can be inherited from parent channels.
**/
struct ACL {
/** Does the ACL apply to this channel? */
bool applyHere;
/** Does the ACL apply to subchannels? */
bool applySubs;
/** Is this ACL inherited from a parent channel? Read-only. */
bool inherited;
/** ID of user this ACL applies to. -1 if using a group name. */
int userid;
/** Group this ACL applies to. Blank if using userid. */
string group;
/** Binary mask of privileges to allow. */
int allow;
/** Binary mask of privileges to deny. */
int deny;
};
/** A single ip mask for a ban.
**/
struct Ban {
/** Address to ban. */
NetAddress address;
/** Number of bits in ban to apply. */
int bits;
/** Username associated with ban. */
string name;
/** Hash of banned user. */
string hash;
/** Reason for ban. */
string reason;
/** Date ban was applied in unix time format. */
int start;
/** Duration of ban. */
int duration;
};
/** A entry in the log.
**/
struct LogEntry {
/** Timestamp in UNIX time_t */
int timestamp;
/** The log message. */
string txt;
};
class Tree;
sequence<Tree> TreeList;
enum ChannelInfo { ChannelDescription, ChannelPosition };
enum UserInfo { UserName, UserEmail, UserComment, UserHash, UserPassword, UserLastActive };
dictionary<int, User> UserMap;
dictionary<int, Channel> ChannelMap;
sequence<Channel> ChannelList;
sequence<User> UserList;
sequence<Group> GroupList;
sequence<ACL> ACLList;
sequence<LogEntry> LogList;
sequence<Ban> BanList;
sequence<int> IdList;
sequence<string> NameList;
dictionary<int, string> NameMap;
dictionary<string, int> IdMap;
sequence<byte> Texture;
dictionary<string, string> ConfigMap;
sequence<string> GroupNameList;
sequence<byte> CertificateDer;
sequence<CertificateDer> CertificateList;
/** User information map.
* Older versions of ice-php can't handle enums as keys. If you are using one of these, replace 'UserInfo' with 'byte'.
*/
dictionary<UserInfo, string> UserInfoMap;
/** User and subchannel state. Read-only.
**/
class Tree {
/** Channel definition of current channel. */
Channel c;
/** List of subchannels. */
TreeList children;
/** Users in this channel. */
UserList users;
};
exception MurmurException {};
/** This is thrown when you specify an invalid session. This may happen if the user has disconnected since your last call to {@link Server.getUsers}. See {@link User.session} */
exception InvalidSessionException extends MurmurException {};
/** This is thrown when you specify an invalid channel id. This may happen if the channel was removed by another provess. It can also be thrown if you try to add an invalid channel. */
exception InvalidChannelException extends MurmurException {};
/** This is thrown when you try to do an operation on a server that does not exist. This may happen if someone has removed the server. */
exception InvalidServerException extends MurmurException {};
/** This happens if you try to fetch user or channel state on a stopped server, if you try to stop an already stopped server or start an already started server. */
exception ServerBootedException extends MurmurException {};
/** This is thrown if {@link Server.start} fails, and should generally be the cause for some concern. */
exception ServerFailureException extends MurmurException {};
/** This is thrown when you specify an invalid userid. */
exception InvalidUserException extends MurmurException {};
/** This is thrown when you try to set an invalid texture. */
exception InvalidTextureException extends MurmurException {};
/** This is thrown when you supply an invalid callback. */
exception InvalidCallbackException extends MurmurException {};
/** This is thrown when you supply the wrong secret in the calling context. */
exception InvalidSecretException extends MurmurException {};
/** This is thrown when the channel operation would excede the channel nesting limit */
exception NestingLimitException extends MurmurException {};
/** Callback interface for servers. You can supply an implementation of this to receive notification
* messages from the server.
* If an added callback ever throws an exception or goes away, it will be automatically removed.
* Please note that all callbacks are done asynchronously; murmur does not wait for the callback to
* complete before continuing processing.
* Note that callbacks are removed when a server is stopped, so you should have a callback for
* {@link MetaCallback.started} which calls {@link Server.addCallback}.
* @see MetaCallback
* @see Server.addCallback
*/
interface ServerCallback {
/** Called when a user connects to the server.
* @param state State of connected user.
*/
idempotent void userConnected(User state);
/** Called when a user disconnects from the server. The user has already been removed, so you can no longer use methods like {@link Server.getState}
* to retrieve the user's state.
* @param state State of disconnected user.
*/
idempotent void userDisconnected(User state);
/** Called when a user state changes. This is called if the user moves, is renamed, is muted, deafened etc.
* @param state New state of user.
*/
idempotent void userStateChanged(User state);
/** Called when user writes a text message
* @param state the User sending the message
* @param message the TextMessage the user has sent
*/
idempotent void userTextMessage(User state, TextMessage message);
/** Called when a new channel is created.
* @param state State of new channel.
*/
idempotent void channelCreated(Channel state);
/** Called when a channel is removed. The channel has already been removed, you can no longer use methods like {@link Server.getChannelState}
* @param state State of removed channel.
*/
idempotent void channelRemoved(Channel state);
/** Called when a new channel state changes. This is called if the channel is moved, renamed or if new links are added.
* @param state New state of channel.
*/
idempotent void channelStateChanged(Channel state);
};
/** Context for actions in the Server menu. */
const int ContextServer = 0x01;
/** Context for actions in the Channel menu. */
const int ContextChannel = 0x02;
/** Context for actions in the User menu. */
const int ContextUser = 0x04;
/** Callback interface for context actions. You need to supply one of these for {@link Server.addContext}.
* If an added callback ever throws an exception or goes away, it will be automatically removed.
* Please note that all callbacks are done asynchronously; murmur does not wait for the callback to
* complete before continuing processing.
*/
interface ServerContextCallback {
/** Called when a context action is performed.
* @param action Action to be performed.
* @param usr User which initiated the action.
* @param session If nonzero, session of target user.
* @param channelid If nonzero, session of target channel.
*/
idempotent void contextAction(string action, User usr, int session, int channelid);
};
/** Callback interface for server authentication. You need to supply one of these for {@link Server.setAuthenticator}.
* If an added callback ever throws an exception or goes away, it will be automatically removed.
* Please note that unlike {@link ServerCallback} and {@link ServerContextCallback}, these methods are called
* synchronously. If the response lags, the entire murmur server will lag.
* Also note that, as the method calls are synchronous, making a call to {@link Server} or {@link Meta} will
* deadlock the server.
*/
interface ServerAuthenticator {
/** Called to authenticate a user. If you do not know the username in question, always return -2 from this
* method to fall through to normal database authentication.
* Note that if authentication succeeds, murmur will create a record of the user in it's database, reserving
* the username and id so it cannot be used for normal database authentication.
* The data in the certificate (name, email addresses etc), as well as the list of signing certificates,
* should only be trusted if certstrong is true.
*
* @param name Username to authenticate.
* @param pw Password to authenticate with.
* @param certificates List of der encoded certificates the user connected with.
* @param certhash Hash of user certificate, as used by murmur internally when matching.
* @param certstrong True if certificate was valid and signed by a trusted CA.
* @param newname Set this to change the username from the supplied one.
* @param groups List of groups on the root channel that the user will be added to for the duration of the connection.
* @return UserID of authenticated user, -1 for authentication failures and -2 for unknown user (fallthrough).
*/
idempotent int authenticate(string name, string pw, CertificateList certificates, string certhash, bool certstrong, out string newname, out GroupNameList groups);
/** Fetch information about a user. This is used to retrieve information like email address, keyhash etc. If you
* want murmur to take care of this information itself, simply return false to fall through.
* @param id User id.
* @param info Information about user. This needs to include at least "name".
* @return true if information is present, false to fall through.
*/
idempotent bool getInfo(int id, out UserInfoMap info);
/** Map a name to a user id.
* @param name Username to map.
* @return User id or -2 for unknown name.
*/
idempotent int nameToId(string name);
/** Map a user id to a username.
* @param id User id to map.
* @return Name of user or empty string for unknown id.
*/
idempotent string idToName(int id);
/** Map a user to a custom Texture.
* @param id User id to map.
* @return User texture or an empty texture for unknwon users or users without textures.
*/
idempotent Texture idToTexture(int id);
};
/** Callback interface for server authentication and registration. This allows you to support both authentication
* and account updating.
* You do not need to implement this if all you want is authentication, you only need this if other scripts
* connected to the same server calls e.g. {@link Server.setTexture}.
* Almost all of these methods support fall through, meaning murmur should continue the operation against its
* own database.
*/
interface ServerUpdatingAuthenticator extends ServerAuthenticator {
/** Register a new user.
* @param info Information about user to register.
* @return User id of new user, -1 for registration failure, or -2 to fall through.
*/
int registerUser(UserInfoMap info);
/** Unregister a user.
* @param id Userid to unregister.
* @return 1 for successfull unregistration, 0 for unsuccessfull unregistration, -1 to fall through.
*/
int unregisterUser(int id);
/** Get a list of registered users matching filter.
* @param filter Substring usernames must contain. If empty, return all registered users.
* @return List of matching registered users.
*/
idempotent NameMap getRegisteredUsers(string filter);
/** Set additional information for user registration.
* @param id Userid of registered user.
* @param info Information to set about user. This should be merged with existing information.
* @return 1 for successfull update, 0 for unsuccessfull update, -1 to fall through.
*/
idempotent int setInfo(int id, UserInfoMap info);
/** Set texture (now called avatar) of user registration.
* @param id registrationId of registered user.
* @param tex New texture.
* @return 1 for successfull update, 0 for unsuccessfull update, -1 to fall through.
*/
idempotent int setTexture(int id, Texture tex);
};
/** Per-server interface. This includes all methods for configuring and altering
* the state of a single virtual server. You can retrieve a pointer to this interface
* from one of the methods in {@link Meta}.
**/
["amd"] interface Server {
/** Shows if the server currently running (accepting users).
*
* @return Run-state of server.
*/
idempotent bool isRunning() throws InvalidSecretException;
/** Start server. */
void start() throws ServerBootedException, ServerFailureException, InvalidSecretException;
/** Stop server.
* Note: Server will be restarted on Murmur restart unless explicitly disabled
* with setConf("boot", false)
*/
void stop() throws ServerBootedException, InvalidSecretException;
/** Delete server and all it's configuration. */
void delete() throws ServerBootedException, InvalidSecretException;
/** Fetch the server id.
*
* @return Unique server id.
*/
idempotent int id() throws InvalidSecretException;
/** Add a callback. The callback will receive notifications about changes to users and channels.
*
* @param cb Callback interface which will receive notifications.
* @see removeCallback
*/
void addCallback(ServerCallback *cb) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
/** Remove a callback.
*
* @param cb Callback interface to be removed.
* @see addCallback
*/
void removeCallback(ServerCallback *cb) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
/** Set external authenticator. If set, all authentications from clients are forwarded to this
* proxy.
*
* @param auth Authenticator object to perform subsequent authentications.
*/
void setAuthenticator(ServerAuthenticator *auth) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
/** Retrieve configuration item.
* @param key Configuration key.
* @return Configuration value. If this is empty, see {@link Meta.getDefaultConf}
*/
idempotent string getConf(string key) throws InvalidSecretException;
/** Retrieve all configuration items.
* @return All configured values. If a value isn't set here, the value from {@link Meta.getDefaultConf} is used.
*/
idempotent ConfigMap getAllConf() throws InvalidSecretException;
/** Set a configuration item.
* @param key Configuration key.
* @param value Configuration value.
*/
idempotent void setConf(string key, string value) throws InvalidSecretException;
/** Set superuser password. This is just a convenience for using {@link updateRegistration} on user id 0.
* @param pw Password.
*/
idempotent void setSuperuserPassword(string pw) throws InvalidSecretException;
/** Fetch log entries.
* @param first Lowest numbered entry to fetch. 0 is the most recent item.
* @param last Last entry to fetch.
* @return List of log entries.
*/
idempotent LogList getLog(int first, int last) throws InvalidSecretException;
/** Fetch length of log
* @return Number of entries in log
*/
idempotent int getLogLen() throws InvalidSecretException;
/** Fetch all users. This returns all currently connected users on the server.
* @return List of connected users.
* @see getState
*/
idempotent UserMap getUsers() throws ServerBootedException, InvalidSecretException;
/** Fetch all channels. This returns all defined channels on the server. The root channel is always channel 0.
* @return List of defined channels.
* @see getChannelState
*/
idempotent ChannelMap getChannels() throws ServerBootedException, InvalidSecretException;
/** Fetch certificate of user. This returns the complete certificate chain of a user.
* @param session Connection ID of user. See {@link User.session}.
* @return Certificate list of user.
*/
idempotent CertificateList getCertificateList(int session) throws ServerBootedException, InvalidSessionException, InvalidSecretException;
/** Fetch all channels and connected users as a tree. This retrieves an easy-to-use representation of the server
* as a tree. This is primarily used for viewing the state of the server on a webpage.
* @return Recursive tree of all channels and connected users.
*/
idempotent Tree getTree() throws ServerBootedException, InvalidSecretException;
/** Fetch all current IP bans on the server.
* @return List of bans.
*/
idempotent BanList getBans() throws ServerBootedException, InvalidSecretException;
/** Set all current IP bans on the server. This will replace any bans already present, so if you want to add a ban, be sure to call {@link getBans} and then
* append to the returned list before calling this method.
* @param bans List of bans.
*/
idempotent void setBans(BanList bans) throws ServerBootedException, InvalidSecretException;
/** Kick a user. The user is not banned, and is free to rejoin the server.
* @param session Connection ID of user. See {@link User.session}.
* @param reason Text message to show when user is kicked.
*/
void kickUser(int session, string reason) throws ServerBootedException, InvalidSessionException, InvalidSecretException;
/** Get state of a single connected user.
* @param session Connection ID of user. See {@link User.session}.
* @return State of connected user.
* @see setState
* @see getUsers
*/
idempotent User getState(int session) throws ServerBootedException, InvalidSessionException, InvalidSecretException;
/** Set user state. You can use this to move, mute and deafen users.
* @param state User state to set.
* @see getState
*/
idempotent void setState(User state) throws ServerBootedException, InvalidSessionException, InvalidChannelException, InvalidSecretException;
/** Send text message to a single user.
* @param session Connection ID of user. See {@link User.session}.
* @param text Message to send.
* @see sendMessageChannel
*/
void sendMessage(int session, string text) throws ServerBootedException, InvalidSessionException, InvalidSecretException;
/** Check if user is permitted to perform action.
* @param session Connection ID of user. See {@link User.session}.
* @param channelid ID of Channel. See {@link Channel.id}.
* @param perm Permission bits to check.
* @return true if any of the permissions in perm were set for the user.
*/
bool hasPermission(int session, int channelid, int perm) throws ServerBootedException, InvalidSessionException, InvalidChannelException, InvalidSecretException;
/** Return users effective permissions
* @param session Connection ID of user. See {@link User.session}.
* @param channelid ID of Channel. See {@link Channel.id}.
* @return bitfield of allowed actions
*/
idempotent int effectivePermissions(int session, int channelid) throws ServerBootedException, InvalidSessionException, InvalidChannelException, InvalidSecretException;
/** Add a context callback. This is done per user, and will add a context menu action for the user.
*
* @param session Session of user which should receive context entry.
* @param action Action string, a unique name to associate with the action.
* @param text Name of action shown to user.
* @param cb Callback interface which will receive notifications.
* @param ctx Context this should be used in. Needs to be one or a combination of {@link ContextServer}, {@link ContextChannel} and {@link ContextUser}.
* @see removeContextCallback
*/
void addContextCallback(int session, string action, string text, ServerContextCallback *cb, int ctx) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
/** Remove a callback.
*
* @param cb Callback interface to be removed. This callback will be removed from all from all users.
* @see addContextCallback
*/
void removeContextCallback(ServerContextCallback *cb) throws ServerBootedException, InvalidCallbackException, InvalidSecretException;
/** Get state of single channel.
* @param channelid ID of Channel. See {@link Channel.id}.
* @return State of channel.
* @see setChannelState
* @see getChannels
*/
idempotent Channel getChannelState(int channelid) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
/** Set state of a single channel. You can use this to move or relink channels.
* @param state Channel state to set.
* @see getChannelState
*/
idempotent void setChannelState(Channel state) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException;
/** Remove a channel and all its subchannels.
* @param channelid ID of Channel. See {@link Channel.id}.
*/
void removeChannel(int channelid) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
/** Add a new channel.
* @param name Name of new channel.
* @param parent Channel ID of parent channel. See {@link Channel.id}.
* @return ID of newly created channel.
*/
int addChannel(string name, int parent) throws ServerBootedException, InvalidChannelException, InvalidSecretException, NestingLimitException;
/** Send text message to channel or a tree of channels.
* @param channelid Channel ID of channel to send to. See {@link Channel.id}.
* @param tree If true, the message will be sent to the channel and all its subchannels.
* @param text Message to send.
* @see sendMessage
*/
void sendMessageChannel(int channelid, bool tree, string text) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
/** Retrieve ACLs and Groups on a channel.
* @param channelid Channel ID of channel to fetch from. See {@link Channel.id}.
* @param acls List of ACLs on the channel. This will include inherited ACLs.
* @param groups List of groups on the channel. This will include inherited groups.
* @param inherit Does this channel inherit ACLs from the parent channel?
*/
idempotent void getACL(int channelid, out ACLList acls, out GroupList groups, out bool inherit) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
/** Set ACLs and Groups on a channel. Note that this will replace all existing ACLs and groups on the channel.
* @param channelid Channel ID of channel to fetch from. See {@link Channel.id}.
* @param acls List of ACLs on the channel.
* @param groups List of groups on the channel.
* @param inherit Should this channel inherit ACLs from the parent channel?
*/
idempotent void setACL(int channelid, ACLList acls, GroupList groups, bool inherit) throws ServerBootedException, InvalidChannelException, InvalidSecretException;
/** Temporarily add a user to a group on a channel. This state is not saved, and is intended for temporary memberships.
* @param channelid Channel ID of channel to add to. See {@link Channel.id}.
* @param session Connection ID of user. See {@link User.session}.
* @param group Group name to add to.
*/
idempotent void addUserToGroup(int channelid, int session, string group) throws ServerBootedException, InvalidChannelException, InvalidSessionException, InvalidSecretException;
/** Remove a user from a temporary group membership on a channel. This state is not saved, and is intended for temporary memberships.
* @param channelid Channel ID of channel to add to. See {@link Channel.id}.
* @param session Connection ID of user. See {@link User.session}.
* @param group Group name to remove from.
*/
idempotent void removeUserFromGroup(int channelid, int session, string group) throws ServerBootedException, InvalidChannelException, InvalidSessionException, InvalidSecretException;
/** Redirect whisper targets for user. If set, whenever a user tries to whisper to group "source", the whisper will be redirected to group "target".
* To remove a redirect pass an empty target string. This is intended for context groups.
* @param session Connection ID of user. See {@link User.session}.
* @param source Group name to redirect from.
* @param target Group name to redirect to.
*/
idempotent void redirectWhisperGroup(int session, string source, string target) throws ServerBootedException, InvalidSessionException, InvalidSecretException;
/** Map a list of {@link User.userid} to a matching name.
* @param List of ids.
* @return Matching list of names, with an empty string representing invalid or unknown ids.
*/
idempotent NameMap getUserNames(IdList ids) throws ServerBootedException, InvalidSecretException;
/** Map a list of user names to a matching id.
* @param List of names.
* @reuturn List of matching ids, with -1 representing invalid or unknown user names.
*/
idempotent IdMap getUserIds(NameList names) throws ServerBootedException, InvalidSecretException;
/** Register a new user.
* @param info Information about new user. Must include at least "name".
* @return The ID of the user. See {@link RegisteredUser.userid}.
*/
int registerUser(UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException;
/** Remove a user registration.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
*/
void unregisterUser(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException;
/** Update the registration for a user. You can use this to set the email or password of a user,
* and can also use it to change the user's name.
* @param registration Updated registration record.
*/
idempotent void updateRegistration(int userid, UserInfoMap info) throws ServerBootedException, InvalidUserException, InvalidSecretException;
/** Fetch registration for a single user.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
* @return Registration record.
*/
idempotent UserInfoMap getRegistration(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException;
/** Fetch a group of registered users.
* @param filter Substring of user name. If blank, will retrieve all registered users.
* @return List of registration records.
*/
idempotent NameMap getRegisteredUsers(string filter) throws ServerBootedException, InvalidSecretException;
/** Verify the password of a user. You can use this to verify a user's credentials.
* @param name User name. See {@link RegisteredUser.name}.
* @param pw User password.
* @return User ID of registered user (See {@link RegisteredUser.userid}), -1 for failed authentication or -2 for unknown usernames.
*/
idempotent int verifyPassword(string name, string pw) throws ServerBootedException, InvalidSecretException;
/** Fetch user texture. Textures are stored as zlib compress()ed 600x60 32-bit BGRA data.
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
* @return Custom texture associated with user or an empty texture.
*/
idempotent Texture getTexture(int userid) throws ServerBootedException, InvalidUserException, InvalidSecretException;
/** Set a user texture (now called avatar).
* @param userid ID of registered user. See {@link RegisteredUser.userid}.
* @param tex Texture (as a Byte-Array) to set for the user, or an empty texture to remove the existing texture.
*/
idempotent void setTexture(int userid, Texture tex) throws ServerBootedException, InvalidUserException, InvalidTextureException, InvalidSecretException;
/** Get virtual server uptime.
* @return Uptime of the virtual server in seconds
*/
idempotent int getUptime() throws ServerBootedException, InvalidSecretException;
};
/** Callback interface for Meta. You can supply an implementation of this to receive notifications
* when servers are stopped or started.
* If an added callback ever throws an exception or goes away, it will be automatically removed.
* Please note that all callbacks are done asynchronously; murmur does not wait for the callback to
* complete before continuing processing.
* @see ServerCallback
* @see Meta.addCallback
*/
interface MetaCallback {
/** Called when a server is started. The server is up and running when this event is sent, so all methods that
* need a running server will work.
* @param srv Interface for started server.
*/
void started(Server *srv);
/** Called when a server is stopped. The server is already stopped when this event is sent, so no methods that
* need a running server will work.
* @param srv Interface for started server.
*/
void stopped(Server *srv);
};
sequence<Server *> ServerList;
/** This is the meta interface. It is primarily used for retrieving the {@link Server} interfaces for each individual server.
**/
["amd"] interface Meta {
/** Fetch interface to specific server.
* @param id Server ID. See {@link Server.getId}.
* @return Interface for specified server, or a null proxy if id is invalid.
*/
idempotent Server *getServer(int id) throws InvalidSecretException;
/** Create a new server. Call {@link Server.getId} on the returned interface to find it's ID.
* @return Interface for new server.
*/
Server *newServer() throws InvalidSecretException;
/** Fetch list of all currently running servers.
* @return List of interfaces for running servers.
*/
idempotent ServerList getBootedServers() throws InvalidSecretException;
/** Fetch list of all defined servers.
* @return List of interfaces for all servers.
*/
idempotent ServerList getAllServers() throws InvalidSecretException;
/** Fetch default configuraion. This returns the configuration items that were set in the configuration file, or
* the built-in default. The individual servers will use these values unless they have been overridden in the
* server specific configuration. The only special case is the port, which defaults to the value defined here +
* the servers ID - 1 (so that virtual server #1 uses the defined port, server #2 uses port+1 etc).
* @return Default configuration of the servers.
*/
idempotent ConfigMap getDefaultConf() throws InvalidSecretException;
/** Fetch version of Murmur.
* @param major Major version.
* @param minor Minor version.
* @param patch Patchlevel.
* @param text Textual representation of version. Note that this may not match the {@link major}, {@link minor} and {@link patch} levels, as it
* may be simply the compile date or the SVN revision. This is usually the text you want to present to users.
*/
idempotent void getVersion(out int major, out int minor, out int patch, out string text);
/** Add a callback. The callback will receive notifications when servers are started or stopped.
*
* @param cb Callback interface which will receive notifications.
*/
void addCallback(MetaCallback *cb) throws InvalidCallbackException, InvalidSecretException;
/** Remove a callback.
*
* @param cb Callback interface to be removed.
*/
void removeCallback(MetaCallback *cb) throws InvalidCallbackException, InvalidSecretException;
/** Get murmur uptime.
* @return Uptime of murmur in seconds
*/
idempotent int getUptime();
/** Get slice file.
* @return Contents of the slice file server compiled with.
*/
idempotent string getSlice();
/** Returns a checksum dict for the slice file.
* @return Checksum dict
*/
idempotent Ice::SliceChecksumDict getSliceChecksums();
};
};

View File

@ -0,0 +1,39 @@
;Database configuration
[database]
;Only tested with MySQL at the moment
lib = MySQLdb
name = alliance_auth
user = allianceserver
password = password
prefix =
host = 127.0.0.1
port = 3306
;Player configuration
[user]
;If you do not already know what it is just leave it as it is
id_offset = 1000000000
;Reject users if the authenticator experiences an internal error during authentication
reject_on_error = True
;Ice configuration
[ice]
host = 127.0.0.1
port = 6502
slice = Murmur.ice
secret =
watchdog = 30
;Murmur configuration
[murmur]
;List of virtual server IDs, empty = all
servers =
;Logging configuration
[log]
; Available loglevels: 10 = DEBUG (default) | 20 = INFO | 30 = WARNING | 40 = ERROR
level =
file = allianceauth.log
[iceraw]
Ice.ThreadPool.Server.Size = 5

798
thirdparty/Mumble/authenticator.py vendored Normal file
View File

@ -0,0 +1,798 @@
#!/usr/bin/env python
# -*- coding: utf-8
# Copyright (C) 2010 Stefan Hacker <dd0t@users.sourceforge.net>
# All rights reserved.
# Adapted by Adarnof for AllianceAuth
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# - Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# - Neither the name of the Mumble Developers nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# `AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# allianceauth.py - Authenticator implementation for password authenticating
# a Murmur server against an AllianceAuth database
#
# Requirements:
# * python >=2.4 and the following python modules:
# * ice-python
# * MySQLdb
# * daemon (when run as a daemon)
#
import sys
import Ice
import thread
import urllib2
import logging
import ConfigParser
from threading import Timer
from optparse import OptionParser
from logging import (debug,
info,
warning,
error,
critical,
exception,
getLogger)
try:
from hashlib import sha1
except ImportError: # python 2.4 compat
from sha import sha as sha1
def x2bool(s):
"""Helper function to convert strings from the config to bool"""
if isinstance(s, bool):
return s
elif isinstance(s, basestring):
return s.lower() in ['1', 'true']
raise ValueError()
#
#--- Default configuration values
#
cfgfile = 'authenticator.ini'
default = {'database':(('lib', str, 'MySQLdb'),
('name', str, 'alliance_auth'),
('user', str, 'allianceserver'),
('password', str, 'password'),
('prefix', str, ''),
('host', str, '127.0.0.1'),
('port', int, 3306)),
'user':(('id_offset', int, 1000000000),
('reject_on_error', x2bool, True)),
'ice':(('host', str, '127.0.0.1'),
('port', int, 6502),
('slice', str, 'Murmur.ice'),
('secret', str, ''),
('watchdog', int, 30)),
'iceraw':None,
'murmur':(('servers', lambda x:map(int, x.split(',')), []),),
'glacier':(('enabled', x2bool, False),
('user', str, 'allianceserver'),
('password', str, 'password'),
('host', str, 'localhost'),
('port', int, '4063')),
'log':(('level', int, logging.DEBUG),
('file', str, 'allianceauth.log'))}
#
#--- Helper classes
#
class config(object):
"""
Small abstraction for config loading
"""
def __init__(self, filename = None, default = None):
if not filename or not default: return
cfg = ConfigParser.ConfigParser()
cfg.optionxform = str
cfg.read(filename)
for h,v in default.iteritems():
if not v:
# Output this whole section as a list of raw key/value tuples
try:
self.__dict__[h] = cfg.items(h)
except ConfigParser.NoSectionError:
self.__dict__[h] = []
else:
self.__dict__[h] = config()
for name, conv, vdefault in v:
try:
self.__dict__[h].__dict__[name] = conv(cfg.get(h, name))
except (ValueError, ConfigParser.NoSectionError, ConfigParser.NoOptionError):
self.__dict__[h].__dict__[name] = vdefault
def entity_decode(string):
"""
Python reverse implementation of php htmlspecialchars
"""
htmlspecialchars = (('"', '&quot;'),
("'", '&#039;'),
('<', '&lt;'),
('>', '&gt'),
('&', '&amp;'))
ret = string
for (s,t) in htmlspecialchars:
ret = ret.replace(t, s)
return ret
def entity_encode(string):
"""
Python implementation of htmlspecialchars
"""
htmlspecialchars = (('&', '&amp;'),
('"', '&quot;'),
("'", '&#039;'),
('<', '&lt;'),
('>', '&gt'))
ret = string
for (s,t) in htmlspecialchars:
ret = ret.replace(s, t)
return ret
class threadDbException(Exception): pass
class threadDB(object):
"""
Small abstraction to handle database connections for multiple
threads
"""
db_connections = {}
def connection(cls):
tid = thread.get_ident()
try:
con = cls.db_connections[tid]
except:
info('Connecting to database server (%s %s:%d %s) for thread %d',
cfg.database.lib, cfg.database.host, cfg.database.port, cfg.database.name, tid)
try:
con = db.connect(host = cfg.database.host,
port = cfg.database.port,
user = cfg.database.user,
passwd = cfg.database.password,
db = cfg.database.name,
charset = 'utf8')
# Transactional engines like InnoDB initiate a transaction even
# on SELECTs-only. Thus, we auto-commit so smfauth gets recent data.
con.autocommit(True)
except db.Error, e:
error('Could not connect to database: %s', str(e))
raise threadDbException()
cls.db_connections[tid] = con
return con
connection = classmethod(connection)
def cursor(cls):
return cls.connection().cursor()
cursor = classmethod(cursor)
def execute(cls, *args, **kwargs):
if "threadDB__retry_execution__" in kwargs:
# Have a magic keyword so we can call ourselves while preventing
# an infinite loop
del kwargs["threadDB__retry_execution__"]
retry = False
else:
retry = True
c = cls.cursor()
try:
c.execute(*args, **kwargs)
except db.OperationalError, e:
error('Database operational error %d: %s', e.args[0], e.args[1])
c.close()
cls.invalidate_connection()
if retry:
# Make sure we only retry once
info('Retrying database operation')
kwargs["threadDB__retry_execution__"] = True
c = cls.execute(*args, **kwargs)
else:
error('Database operation failed ultimately')
raise threadDbException()
return c
execute = classmethod(execute)
def invalidate_connection(cls):
tid = thread.get_ident()
con = cls.db_connections.pop(tid, None)
if con:
debug('Invalidate connection to database for thread %d', tid)
con.close()
invalidate_connection = classmethod(invalidate_connection)
def disconnect(cls):
while cls.db_connections:
tid, con = cls.db_connections.popitem()
debug('Close database connection for thread %d', tid)
con.close()
disconnect = classmethod(disconnect)
def do_main_program():
#
#--- Authenticator implementation
# All of this has to go in here so we can correctly daemonize the tool
# without loosing the file descriptors opened by the Ice module
slicedir = Ice.getSliceDir()
if not slicedir:
slicedir = ["-I/usr/share/Ice/slice", "-I/usr/share/slice"]
else:
slicedir = ['-I' + slicedir]
Ice.loadSlice('', slicedir + [cfg.ice.slice])
import Murmur
class allianceauthauthenticatorApp(Ice.Application):
def run(self, args):
self.shutdownOnInterrupt()
if not self.initializeIceConnection():
return 1
if cfg.ice.watchdog > 0:
self.failedWatch = True
self.checkConnection()
# Serve till we are stopped
self.communicator().waitForShutdown()
self.watchdog.cancel()
if self.interrupted():
warning('Caught interrupt, shutting down')
threadDB.disconnect()
return 0
def initializeIceConnection(self):
"""
Establishes the two-way Ice connection and adds the authenticator to the
configured servers
"""
ice = self.communicator()
if cfg.ice.secret:
debug('Using shared ice secret')
ice.getImplicitContext().put("secret", cfg.ice.secret)
elif not cfg.glacier.enabled:
warning('Consider using an ice secret to improve security')
if cfg.glacier.enabled:
#info('Connecting to Glacier2 server (%s:%d)', glacier_host, glacier_port)
error('Glacier support not implemented yet')
#TODO: Implement this
info('Connecting to Ice server (%s:%d)', cfg.ice.host, cfg.ice.port)
base = ice.stringToProxy('Meta:tcp -h %s -p %d' % (cfg.ice.host, cfg.ice.port))
self.meta = Murmur.MetaPrx.uncheckedCast(base)
adapter = ice.createObjectAdapterWithEndpoints('Callback.Client', 'tcp -h %s' % cfg.ice.host)
adapter.activate()
metacbprx = adapter.addWithUUID(metaCallback(self))
self.metacb = Murmur.MetaCallbackPrx.uncheckedCast(metacbprx)
authprx = adapter.addWithUUID(allianceauthauthenticator())
self.auth = Murmur.ServerUpdatingAuthenticatorPrx.uncheckedCast(authprx)
return self.attachCallbacks()
def attachCallbacks(self, quiet = False):
"""
Attaches all callbacks for meta and authenticators
"""
# Ice.ConnectionRefusedException
#debug('Attaching callbacks')
try:
if not quiet: info('Attaching meta callback')
self.meta.addCallback(self.metacb)
for server in self.meta.getBootedServers():
if not cfg.murmur.servers or server.id() in cfg.murmur.servers:
if not quiet: info('Setting authenticator for virtual server %d', server.id())
server.setAuthenticator(self.auth)
except (Murmur.InvalidSecretException, Ice.UnknownUserException, Ice.ConnectionRefusedException), e:
if isinstance(e, Ice.ConnectionRefusedException):
error('Server refused connection')
elif isinstance(e, Murmur.InvalidSecretException) or \
isinstance(e, Ice.UnknownUserException) and (e.unknown == 'Murmur::InvalidSecretException'):
error('Invalid ice secret')
else:
# We do not actually want to handle this one, re-raise it
raise e
self.connected = False
return False
self.connected = True
return True
def checkConnection(self):
"""
Tries reapplies all callbacks to make sure the authenticator
survives server restarts and disconnects.
"""
#debug('Watchdog run')
try:
if not self.attachCallbacks(quiet = not self.failedWatch):
self.failedWatch = True
else:
self.failedWatch = False
except Ice.Exception, e:
error('Failed connection check, will retry in next watchdog run (%ds)', cfg.ice.watchdog)
debug(str(e))
self.failedWatch = True
# Renew the timer
self.watchdog = Timer(cfg.ice.watchdog, self.checkConnection)
self.watchdog.start()
def checkSecret(func):
"""
Decorator that checks whether the server transmitted the right secret
if a secret is supposed to be used.
"""
if not cfg.ice.secret:
return func
def newfunc(*args, **kws):
if 'current' in kws:
current = kws["current"]
else:
current = args[-1]
if not current or 'secret' not in current.ctx or current.ctx['secret'] != cfg.ice.secret:
error('Server transmitted invalid secret. Possible injection attempt.')
raise Murmur.InvalidSecretException()
return func(*args, **kws)
return newfunc
def fortifyIceFu(retval = None, exceptions = (Ice.Exception,)):
"""
Decorator that catches exceptions,logs them and returns a safe retval
value. This helps preventing the authenticator getting stuck in
critical code paths. Only exceptions that are instances of classes
given in the exceptions list are not caught.
The default is to catch all non-Ice exceptions.
"""
def newdec(func):
def newfunc(*args, **kws):
try:
return func(*args, **kws)
except Exception, e:
catch = True
for ex in exceptions:
if isinstance(e, ex):
catch = False
break
if catch:
critical('Unexpected exception caught')
exception(e)
return retval
raise
return newfunc
return newdec
class metaCallback(Murmur.MetaCallback):
def __init__(self, app):
Murmur.MetaCallback.__init__(self)
self.app = app
@fortifyIceFu()
@checkSecret
def started(self, server, current = None):
"""
This function is called when a virtual server is started
and makes sure an authenticator gets attached if needed.
"""
if not cfg.murmur.servers or server.id() in cfg.murmur.servers:
info('Setting authenticator for virtual server %d', server.id())
try:
server.setAuthenticator(app.auth)
# Apparently this server was restarted without us noticing
except (Murmur.InvalidSecretException, Ice.UnknownUserException), e:
if hasattr(e, "unknown") and e.unknown != "Murmur::InvalidSecretException":
# Special handling for Murmur 1.2.2 servers with invalid slice files
raise e
error('Invalid ice secret')
return
else:
debug('Virtual server %d got started', server.id())
@fortifyIceFu()
@checkSecret
def stopped(self, server, current = None):
"""
This function is called when a virtual server is stopped
"""
if self.app.connected:
# Only try to output the server id if we think we are still connected to prevent
# flooding of our thread pool
try:
if not cfg.murmur.servers or server.id() in cfg.murmur.servers:
info('Authenticated virtual server %d got stopped', server.id())
else:
debug('Virtual server %d got stopped', server.id())
return
except Ice.ConnectionRefusedException:
self.app.connected = False
debug('Server shutdown stopped a virtual server')
if cfg.user.reject_on_error: # Python 2.4 compat
authenticateFortifyResult = (-1, None, None)
else:
authenticateFortifyResult = (-2, None, None)
class allianceauthauthenticator(Murmur.ServerUpdatingAuthenticator):
texture_cache = {}
def __init__(self):
Murmur.ServerUpdatingAuthenticator.__init__(self)
@fortifyIceFu(authenticateFortifyResult)
@checkSecret
def authenticate(self, name, pw, certlist, certhash, strong, current = None):
"""
This function is called to authenticate a user
"""
# Search for the user in the database
FALL_THROUGH = -2
AUTH_REFUSED = -1
if name == 'SuperUser':
debug('Forced fall through for SuperUser')
return (FALL_THROUGH, None, None)
try:
sql = 'SELECT id, pwhash, groups FROM %sservices_mumbleuser WHERE username = %%s' % cfg.database.prefix
cur = threadDB.execute(sql, [name])
except threadDbException:
return (FALL_THROUGH, None, None)
res = cur.fetchone()
cur.close()
if not res:
info('Fall through for unknown user "%s"', name)
return (FALL_THROUGH, None, None)
uid, upwhash, ugroups = res
groups = ugroups.split(',')
if allianceauth_check_hash(pw, upwhash):
info('User authenticated: "%s" (%d)', name, uid + cfg.user.id_offset)
debug('Group memberships: %s', str(groups))
return (uid + cfg.user.id_offset, entity_decode(name), groups)
info('Failed authentication attempt for user: "%s" (%d)', name, uid + cfg.user.id_offset)
return (AUTH_REFUSED, None, None)
@fortifyIceFu((False, None))
@checkSecret
def getInfo(self, id, current = None):
"""
Gets called to fetch user specific information
"""
# We do not expose any additional information so always fall through
debug('getInfo for %d -> denied', id)
return (False, None)
@fortifyIceFu(-2)
@checkSecret
def nameToId(self, name, current = None):
"""
Gets called to get the id for a given username
"""
FALL_THROUGH = -2
if name == 'SuperUser':
debug('nameToId SuperUser -> forced fall through')
return FALL_THROUGH
try:
sql = 'SELECT id FROM %sservices_mumbleuser WHERE username = %%s' % cfg.database.prefix
cur = threadDB.execute(sql, [name])
except threadDbException:
return FALL_THROUGH
res = cur.fetchone()
cur.close()
if not res:
debug('nameToId %s -> ?', name)
return FALL_THROUGH
debug('nameToId %s -> %d', name, (res[0] + cfg.user.id_offset))
return res[0] + cfg.user.id_offset
@fortifyIceFu("")
@checkSecret
def idToName(self, id, current = None):
"""
Gets called to get the username for a given id
"""
FALL_THROUGH = ""
# Make sure the ID is in our range and transform it to the actual smf user id
if id < cfg.user.id_offset:
return FALL_THROUGH
bbid = id - cfg.user.id_offset
# Fetch the user from the database
try:
sql = 'SELECT username FROM %sservices_mumbleuser WHERE id = %%s' % cfg.database.prefix
cur = threadDB.execute(sql, [bbid])
except threadDbException:
return FALL_THROUGH
res = cur.fetchone()
cur.close()
if res:
if res[0] == 'SuperUser':
debug('idToName %d -> "SuperUser" catched')
return FALL_THROUGH
debug('idToName %d -> "%s"', id, res[0])
return res[0]
debug('idToName %d -> ?', id)
return FALL_THROUGH
@fortifyIceFu("")
@checkSecret
def idToTexture(self, id, current = None):
"""
Gets called to get the corresponding texture for a user
"""
FALL_THROUGH = ""
debug('idToTexture "%s" -> fall through', id)
return FALL_THROUGH
@fortifyIceFu(-2)
@checkSecret
def registerUser(self, name, current = None):
"""
Gets called when the server is asked to register a user.
"""
FALL_THROUGH = -2
debug('registerUser "%s" -> fall through', name)
return FALL_THROUGH
@fortifyIceFu(-1)
@checkSecret
def unregisterUser(self, id, current = None):
"""
Gets called when the server is asked to unregister a user.
"""
FALL_THROUGH = -1
# Return -1 to fall through to internal server database, we will not modify the smf database
# but we can make murmur delete all additional information it got this way.
debug('unregisterUser %d -> fall through', id)
return FALL_THROUGH
@fortifyIceFu({})
@checkSecret
def getRegisteredUsers(self, filter, current = None):
"""
Returns a list of usernames in the AllianceAuth database which contain
filter as a substring.
"""
if not filter:
filter = '%'
try:
sql = 'SELECT id, username FROM %sservices_mumbleuser WHERE username LIKE %%s' % cfg.database.prefix
cur = threadDB.execute(sql, [filter])
except threadDbException:
return {}
res = cur.fetchall()
cur.close()
if not res:
debug('getRegisteredUsers -> empty list for filter "%s"', filter)
return {}
debug ('getRegisteredUsers -> %d results for filter "%s"', len(res), filter)
return dict([(a + cfg.user.id_offset, b) for a,b in res])
@fortifyIceFu(-1)
@checkSecret
def setInfo(self, id, info, current = None):
"""
Gets called when the server is supposed to save additional information
about a user to his database
"""
FALL_THROUGH = -1
# Return -1 to fall through to the internal server handler. We must not modify
# the smf database so the additional information is stored in murmurs database
debug('setInfo %d -> fall through', id)
return FALL_THROUGH
@fortifyIceFu(-1)
@checkSecret
def setTexture(self, id, texture, current = None):
"""
Gets called when the server is asked to update the user texture of a user
"""
FALL_THROUGH = -1
debug('setTexture %d -> fall through', id)
return FALL_THROUGH
class CustomLogger(Ice.Logger):
"""
Logger implementation to pipe Ice log messages into
our own log
"""
def __init__(self):
Ice.Logger.__init__(self)
self._log = getLogger('Ice')
def _print(self, message):
self._log.info(message)
def trace(self, category, message):
self._log.debug('Trace %s: %s', category, message)
def warning(self, message):
self._log.warning(message)
def error(self, message):
self._log.error(message)
#
#--- Start of authenticator
#
info('Starting AllianceAuth mumble authenticator')
initdata = Ice.InitializationData()
initdata.properties = Ice.createProperties([], initdata.properties)
for prop, val in cfg.iceraw:
initdata.properties.setProperty(prop, val)
initdata.properties.setProperty('Ice.ImplicitContext', 'Shared')
initdata.properties.setProperty('Ice.Default.EncodingVersion', '1.0')
initdata.logger = CustomLogger()
app = allianceauthauthenticatorApp()
state = app.main(sys.argv[:1], initData = initdata)
info('Shutdown complete')
#
#--- Python implementation of the AllianceAuth MumbleUser hash function
#
def allianceauth_check_hash(password, hash):
"""
Python implementation of the smf check hash function
"""
return sha1(password).hexdigest() == hash
#
#--- Start of program
#
if __name__ == '__main__':
# Parse commandline options
parser = OptionParser()
parser.add_option('-i', '--ini',
help = 'load configuration from INI', default = cfgfile)
parser.add_option('-v', '--verbose', action='store_true', dest = 'verbose',
help = 'verbose output [default]', default = True)
parser.add_option('-q', '--quiet', action='store_false', dest = 'verbose',
help = 'only error output')
parser.add_option('-d', '--daemon', action='store_true', dest = 'force_daemon',
help = 'run as daemon', default = False)
parser.add_option('-a', '--app', action='store_true', dest = 'force_app',
help = 'do not run as daemon', default = False)
(option, args) = parser.parse_args()
if option.force_daemon and option.force_app:
parser.print_help()
sys.exit(1)
# Load configuration
try:
cfg = config(option.ini, default)
except Exception, e:
print>>sys.stderr, 'Fatal error, could not load config file from "%s"' % cfgfile
sys.exit(1)
try:
db = __import__(cfg.database.lib)
except ImportError, e:
print>>sys.stderr, 'Fatal error, could not import database library "%s", '\
'please install the missing dependency and restart the authenticator' % cfg.database.lib
sys.exit(1)
# Initialize logger
if cfg.log.file:
try:
logfile = open(cfg.log.file, 'a')
except IOError, e:
#print>>sys.stderr, str(e)
print>>sys.stderr, 'Fatal error, could not open logfile "%s"' % cfg.log.file
sys.exit(1)
else:
logfile = logging.sys.stderr
if option.verbose:
level = cfg.log.level
else:
level = logging.ERROR
logging.basicConfig(level = level,
format='%(asctime)s %(levelname)s %(message)s',
stream = logfile)
# As the default try to run as daemon. Silently degrade to running as a normal application if this fails
# unless the user explicitly defined what he expected with the -a / -d parameter.
try:
if option.force_app:
raise ImportError # Pretend that we couldn't import the daemon lib
import daemon
except ImportError:
if option.force_daemon:
print>>sys.stderr, 'Fatal error, could not daemonize process due to missing "daemon" library, ' \
'please install the missing dependency and restart the authenticator'
sys.exit(1)
do_main_program()
else:
context = daemon.DaemonContext(working_directory = sys.path[0],
stderr = logfile)
context.__enter__()
try:
do_main_program()
finally:
context.__exit__(None, None, None)