From e6b08fca887a650c6076cf8cc95dc46e8070f489 Mon Sep 17 00:00:00 2001 From: Meletis Flevarakis Date: Sun, 12 Jun 2016 00:22:58 +0300 Subject: [PATCH] XenForo integration initial version (#459) * XenForo integration beta * Removing debug loggers --- alliance_auth/settings.py.example | 12 ++ alliance_auth/urls.py | 9 ++ authentication/managers.py | 12 ++ authentication/models.py | 2 + celerytask/tasks.py | 1 + services/managers/xenforo_manager.py | 137 +++++++++++++++++++++++ services/views.py | 74 ++++++++++++ stock/templates/registered/services.html | 58 ++++++++++ util/common_task.py | 6 + util/context_processors.py | 2 + 10 files changed, 313 insertions(+) create mode 100644 services/managers/xenforo_manager.py diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 632b5190..b5cc70ac 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -264,6 +264,7 @@ BLUE_ALLIANCE_GROUPS = 'True' == os.environ.get('AA_BLUE_ALLIANCE_GROUPS', 'Fals # ENABLE_AUTH_SMF - Enable SMF forum support in the auth for auth'd members # ENABLE_AUTH_MARKET = Enable Alliance Market support in auth for auth'd members # ENABLE_AUTH_PATHFINDER = Enable Alliance Pathfinder suppor in auth for auth'd members +# ENABLE_AUTH_XENFORO = Enable XenForo forums support in the auth for auth'd members ######################### ENABLE_AUTH_FORUM = 'True' == os.environ.get('AA_ENABLE_AUTH_FORUM', 'False') ENABLE_AUTH_JABBER = 'True' == os.environ.get('AA_ENABLE_AUTH_JABBER', 'False') @@ -276,6 +277,7 @@ ENABLE_AUTH_IPS4 = 'True' == os.environ.get('AA_ENABLE_AUTH_IPS4', 'False') ENABLE_AUTH_SMF = 'True' == os.environ.get('AA_ENABLE_AUTH_SMF', 'False') ENABLE_AUTH_MARKET = 'True' == os.environ.get('AA_ENABLE_AUTH_MARKET', 'False') ENABLE_AUTH_PATHFINDER = 'True' == os.environ.get('AA_ENABLE_AUTH_PATHFINDER', 'False') +ENABLE_AUTH_XENFORO = 'True' == os.environ.get('AA_ENABLE_AUTH_XENFORO', 'False') ##################### # Blue service Setup @@ -291,6 +293,7 @@ ENABLE_AUTH_PATHFINDER = 'True' == os.environ.get('AA_ENABLE_AUTH_PATHFINDER', ' # ENABLE_BLUE_SMF - Enable SMF forum support in the auth for blues # ENABLE_BLUE_MARKET - Enable Alliance Market in the auth for blues # ENABLE_BLUE_PATHFINDER = Enable Pathfinder support in the auth for blues +# ENABLE_BLUE_XENFORO = Enable XenForo forum support in the auth for blue ##################### BLUE_STANDING = float(os.environ.get('AA_BLUE_STANDING', '5.0')) ENABLE_BLUE_FORUM = 'True' == os.environ.get('AA_ENABLE_BLUE_FORUM', 'False') @@ -304,6 +307,7 @@ ENABLE_BLUE_IPS4 = 'True' == os.environ.get('AA_ENABLE_BLUE_IPS4', 'False') ENABLE_BLUE_SMF = 'True' == os.environ.get('AA_ENABLE_BLUE_SMF', 'False') ENABLE_BLUE_MARKET = 'True' == os.environ.get('AA_ENABLE_BLUE_MARKET', 'False') ENABLE_BLUE_PATHFINDER = 'True' == os.environ.get('AA_ENABLE_BLUE_PATHFINDER', 'False') +ENABLE_BLUE_XENFORO = 'True' == os.environ.get('AA_ENABLE_BLUE_XENFORO', 'False') ######################### # Corp Configuration @@ -374,6 +378,14 @@ IPBOARD_ENDPOINT = os.environ.get('AA_IPBOARD_ENDPOINT', 'yourdomain.com/interfa IPBOARD_APIKEY = os.environ.get('AA_IPBOARD_APIKEY', 'somekeyhere') IPBOARD_APIMODULE = 'aa' +######################## +# XenForo Configuration +######################## +XENFORO_ENDPOINT = os.environ.get('AA_XENFORO_ENDPOINT', 'yourdomain.com/api.php') +XENFORO_DEFAULT_GROUP = os.environ.get('AA_XENFORO_DEFAULT_GROUP', 0) +XENFORO_APIKEY = os.environ.get('AA_XENFORO_APIKEY', 'yourapikey') +##################### + ###################### # Jabber Configuration ###################### diff --git a/alliance_auth/urls.py b/alliance_auth/urls.py index cb21e06a..def77686 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -123,6 +123,15 @@ urlpatterns = patterns('', name='auth_reset_ipboard_password'), url(r'^set_ipboard_password/$', 'services.views.set_ipboard_password', name='auth_set_ipboard_password'), + # XenForo service control + url(r'^activate_xenforo/$', 'services.views.activate_xenforo_forum', + name='auth_activate_xenforo'), + url(r'^deactivate_xenforo/$', 'services.views.deactivate_xenforo_forum', + name='auth_deactivate_xenforo'), + url(r'^reset_xenforo_password/$', 'services.views.reset_xenforo_password', + name='auth_reset_xenforo_password'), + url(r'^set_xenforo_password/$', 'services.views.set_xenforo_password', name='auth_set_xenforo_password'), + # Teamspeak3 service control url(r'^activate_teamspeak3/$', 'services.views.activate_teamspeak3', name='auth_activate_teamspeak3'), diff --git a/authentication/managers.py b/authentication/managers.py index 84f867bf..1ab279b0 100755 --- a/authentication/managers.py +++ b/authentication/managers.py @@ -90,6 +90,18 @@ class AuthServicesInfoManager: else: logger.error("Failed to update user %s ipboard info: user does not exist." % user) + @staticmethod + def update_user_xenforo_info(username, password, user): + if User.objects.filter(username=user.username).exists(): + logger.debug("Updating user %s xenforo info: uername %s" % (user, username)) + authserviceinfo = AuthServicesInfoManager.__get_or_create(user) + authserviceinfo.xenforo_username = username + authserviceinfo.xenforo_password = password + authserviceinfo.save(update_fields=['xenforo_username', 'xenforo_password']) + logger.info("Updated user %s xenforo info in authservicesinfo model." % user) + else: + logger.error("Failed to update user %s xenforo info: user does not exist." % user) + @staticmethod def update_user_teamspeak3_info(uid, perm_key, user): if User.objects.filter(username=user.username).exists(): diff --git a/authentication/models.py b/authentication/models.py index 11b51097..aaed9119 100755 --- a/authentication/models.py +++ b/authentication/models.py @@ -5,6 +5,8 @@ from django.contrib.auth.models import User class AuthServicesInfo(models.Model): ipboard_username = models.CharField(max_length=254, blank=True, default="") ipboard_password = models.CharField(max_length=254, blank=True, default="") + xenforo_username = models.CharField(max_length=254, blank=True, default="") + xenforo_password = models.CharField(max_length=254, blank=True, default="") forum_username = models.CharField(max_length=254, blank=True, default="") forum_password = models.CharField(max_length=254, blank=True, default="") jabber_username = models.CharField(max_length=254, blank=True, default="") diff --git a/celerytask/tasks.py b/celerytask/tasks.py index b4b19324..c4128331 100644 --- a/celerytask/tasks.py +++ b/celerytask/tasks.py @@ -9,6 +9,7 @@ from services.managers.openfire_manager import OpenfireManager from services.managers.mumble_manager import MumbleManager from services.managers.phpbb3_manager import Phpbb3Manager from services.managers.ipboard_manager import IPBoardManager +from services.managers.xenforo_manager import XenForoManager from services.managers.teamspeak3_manager import Teamspeak3Manager from services.managers.discord_manager import DiscordManager, DiscordAPIManager from services.managers.discourse_manager import DiscourseManager diff --git a/services/managers/xenforo_manager.py b/services/managers/xenforo_manager.py new file mode 100644 index 00000000..18050201 --- /dev/null +++ b/services/managers/xenforo_manager.py @@ -0,0 +1,137 @@ +import os +import requests +import json + +from django.conf import settings + +import logging + +logger = logging.getLogger(__name__) + +class XenForoManager: + def __init__(self): + if not settings.XENFORO_ENDPOINT: + logger.debug("Could not find XenForo endpoint") + if not settings.XENFORO_APIKEY: + logger.debug("XenForo API Key not found") + pass + + @staticmethod + def __sanitize_username(username): + sanitized = username.replace(" ", "_") + return sanitized + + @staticmethod + def __generate_password(): + return os.urandom(8).encode('hex') + + @staticmethod + def exec_http_request(http_params): + default_params = { + 'hash': settings.XENFORO_APIKEY + } + http_params.update(default_params) + r = requests.get(settings.XENFORO_ENDPOINT, params=http_params) + return r + + @staticmethod + def add_user(username, email): + + sanitized = XenForoManager.__sanitize_username(username) + password = XenForoManager.__generate_password(); + + data = { + 'action': 'register', + 'username': sanitized, + 'password': password, + 'email': email, + 'group': settings.XENFORO_DEFAULT_GROUP, + 'visible': '1', + 'user_state': 'valid' + } + + r = XenForoManager.exec_http_request(data) + + # check if the user already exist but was disabled + if r.status_code != 200: + if json.loads(r.text)['error'] == 7: + data = XenForoManager.reactivate_user(sanitized) + return data + + response = { + 'response': { + 'message': r.text, + 'status_code': r.status_code + } + } + data.update(response) + return data + + @staticmethod + def reset_password(username): + + password = XenForoManager.__generate_password(); + + data = { + 'action': 'editUser', + 'user': username, + 'password': password + } + + r = XenForoManager.exec_http_request(data) + + response = { + 'response': { + 'message': r.text, + 'status_code': r.status_code + } + } + data.update(response) + return data + + @staticmethod + def disable_user(username): + data = { + 'action': 'editUser', + 'user': username, + 'remove_groups': settings.XENFORO_DEFAULT_GROUP + } + r = XenForoManager.exec_http_request(data) + return r + + @staticmethod + def reactivate_user(username): + data = { + 'action': 'editUser', + 'user': username, + 'add_groups': settings.XENFORO_DEFAULT_GROUP, + 'password': XenForoManager.__generate_password() + } + r = XenForoManager.exec_http_request(data) + response = { + 'response': { + 'message': r.text, + 'status_code': r.status_code + }, + 'username': username + } + data.update(response) + return data + + @staticmethod + def update_user_password(username, raw_password): + data = { + 'action': 'editUser', + 'user': username, + 'password': raw_password + } + r = XenForoManager.exec_http_request(data) + response = { + 'response': { + 'message': r.text, + 'status_code': r.status_code + }, + 'username': username + } + data.update(response) + return data \ No newline at end of file diff --git a/services/views.py b/services/views.py index 1fa5e797..2ff4854e 100755 --- a/services/views.py +++ b/services/views.py @@ -12,6 +12,7 @@ from managers.openfire_manager import OpenfireManager from managers.phpbb3_manager import Phpbb3Manager from managers.mumble_manager import MumbleManager from managers.ipboard_manager import IPBoardManager +from managers.xenforo_manager import XenForoManager from managers.teamspeak3_manager import Teamspeak3Manager from managers.discord_manager import DiscordManager from managers.discourse_manager import DiscourseManager @@ -178,6 +179,79 @@ def reset_forum_password(request): logger.error("Unsuccessful attempt to reset forum password for user %s" % request.user) return HttpResponseRedirect("/dashboard") +@login_required +@user_passes_test(service_blue_alliance_test) +def activate_xenforo_forum(request): + logger.debug("activate_xenforo_forum called by user %s" % request.user) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + character = EveManager.get_character_by_id(authinfo.main_char_id) + logger.debug("Adding XenForo user for user %s with main character %s" % (request.user, character)) + result = XenForoManager.add_user(character.character_name, request.user.email) + # Based on XenAPI's response codes + if result['response']['status_code'] == 200: + logger.info("Updated authserviceinfo for user %s with XenForo credentials. Updating groups." % request.user) + AuthServicesInfoManager.update_user_xenforo_info(result['username'], result['password'], request.user) + return HttpResponseRedirect("/services/") + logger.error("Unsuccesful attempt to activate xenforo for user %s" % request.user) + return HttpResponseRedirect("/dashboard") + +@login_required +@user_passes_test(service_blue_alliance_test) +def deactivate_xenforo_forum(request): + logger.debug("deactivate_xenforo_forum called by user %s" % request.user) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + result = XenForoManager.disable_user(authinfo.xenforo_username) + if result.status_code == 200: + AuthServicesInfoManager.update_user_xenforo_info("", "", request.user) + logger.info("Succesfully deactivated XenForo for user %s" % request.user) + return HttpResponseRedirect("/services/") + return HttpResponseRedirect("/dashboard") + +@login_required +@user_passes_test(service_blue_alliance_test) +def reset_xenforo_password(request): + logger.debug("reset_xenforo_password called by user %s" % request.user) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + character = EveManager.get_character_by_id(authinfo.main_char_id) + result = XenForoManager.reset_password(authinfo.xenforo_username) + # Based on XenAPI's response codes + if result['response']['status_code'] == 200: + AuthServicesInfoManager.update_user_xenforo_info(authinfo.xenforo_username, result['password'], request.user) + logger.info("Succesfully reset XenForo password for user %s" % request.user) + return HttpResponseRedirect("/services/") + logger.error("Unsuccessful attempt to reset XenForo password for user %s" % request.user) + return HttpResponseRedirect("/dashboard") + +@login_required +@user_passes_test(service_blue_alliance_test) +def set_xenforo_password(request): + logger.debug("set_xenforo_password called by user %s" % request.user) + error = None + if request.method == 'POST': + logger.debug("Received POST request with form.") + form = ServicePasswordForm(request.POST) + logger.debug("Form is valid: %s" % form.is_valid()) + if form.is_valid(): + password = form.cleaned_data['password'] + logger.debug("Form contains password of length %s" % len(password)) + authinfo = AuthServicesInfoManager.get_auth_service_info(request.user) + result = XenForoManager.update_user_password(authinfo.xenforo_username, password) + if result['response']['status_code'] == 200: + AuthServicesInfoManager.update_user_xenforo_info(authinfo.xenforo_username, result['password'], request.user) + logger.info("Succesfully reset XenForo password for user %s" % request.user) + return HttpResponseRedirect("/services/") + else: + logger.error("Failed to install custom XenForo password for user %s" % request.user) + error = "Failed to install custom password." + else: + error = "Invalid password provided" + else: + logger.debug("Request is not type POST - providing empty form.") + form = ServicePasswordForm() + + logger.debug("Rendering form for user %s" % request.user) + context = {'form': form, 'service': 'Forum'} + return render_to_response('registered/service_password.html', context, context_instance=RequestContext(request)) @login_required @user_passes_test(service_blue_alliance_test) diff --git a/stock/templates/registered/services.html b/stock/templates/registered/services.html index a98bedd8..d6b26a04 100755 --- a/stock/templates/registered/services.html +++ b/stock/templates/registered/services.html @@ -105,6 +105,35 @@ {% endif %} + {% if ENABLE_BLUE_XENFORO %} + + XenForo Forums + {{ authinfo.xenforo_username }} + {{ authinfo.xenforo_password }} + {{ FORUM_URL }} + + {% ifequal authinfo.xenforo_username "" %} + + + + {% else %} + + + + + + + + + + {% endifequal %} + + + {% endif %} {% if ENABLE_BLUE_MARKET %} Alliance Market @@ -430,6 +459,35 @@ {% endif %} + {% if ENABLE_AUTH_XENFORO %} + + XenForo Forums + {{ authinfo.xenforo_username }} + {{ authinfo.xenforo_password }} + {{ FORUM_URL }} + + {% ifequal authinfo.xenforo_username "" %} + + + + {% else %} + + + + + + + + + + {% endifequal %} + + + {% endif %} {% if ENABLE_AUTH_MARKET %} Alliance Market diff --git a/util/common_task.py b/util/common_task.py index 55b020f4..efdde479 100755 --- a/util/common_task.py +++ b/util/common_task.py @@ -8,6 +8,7 @@ from services.managers.mumble_manager import MumbleManager from services.managers.ipboard_manager import IPBoardManager from services.managers.teamspeak3_manager import Teamspeak3Manager from services.managers.discord_manager import DiscordManager +from services.managers.xenforo_manager import XenForoManager import logging @@ -72,6 +73,11 @@ def deactivate_services(user): DiscordManager.delete_user(authinfo.discord_uid) AuthServicesInfoManager.update_user_discord_info("", user) change = True + if authinfo.xenforo_username and authinfo.xenforo_password != "": + logger.debug("User %s has a XenForo account %s. Deleting." % (user, authinfo.xenforo_username)) + XenForoManager.disable_user(authinfo.xenforo_username) + AuthServicesInfoManager.update_user_xenforo_info("", "", user) + change = True if change: notify(user, "Services Disabled", message="Your services accounts have been disabled.", level="danger") diff --git a/util/context_processors.py b/util/context_processors.py index 83b52eb4..2c55052a 100755 --- a/util/context_processors.py +++ b/util/context_processors.py @@ -40,6 +40,7 @@ def domain_url(request): 'ENABLE_AUTH_DISCOURSE': settings.ENABLE_AUTH_DISCOURSE, 'ENABLE_AUTH_IPS4': settings.ENABLE_AUTH_IPS4, 'ENABLE_AUTH_SMF': settings.ENABLE_AUTH_SMF, + 'ENABLE_AUTH_XENFORO': settings.ENABLE_AUTH_XENFORO, 'ENABLE_AUTH_MARKET': settings.ENABLE_AUTH_MARKET, 'ENABLE_AUTH_PATHFINDER': settings.ENABLE_AUTH_PATHFINDER, 'ENABLE_BLUE_JABBER': settings.ENABLE_BLUE_JABBER, @@ -53,6 +54,7 @@ def domain_url(request): 'ENABLE_BLUE_SMF': settings.ENABLE_BLUE_SMF, 'ENABLE_BLUE_MARKET': settings.ENABLE_BLUE_MARKET, 'ENABLE_BLUE_PATHFINDER': settings.ENABLE_BLUE_PATHFINDER, + 'ENABLE_BLUE_XENFORO': settings.ENABLE_BLUE_XENFORO, 'TEAMSPEAK3_PUBLIC_URL': settings.TEAMSPEAK3_PUBLIC_URL, 'JACK_KNIFE_URL': settings.JACK_KNIFE_URL, 'DISCORD_SERVER_ID': settings.DISCORD_SERVER_ID,