mirror of
https://gitlab.com/allianceauth/allianceauth.git
synced 2025-07-13 14:30:17 +02:00
discourse API with external package
This commit is contained in:
parent
6bcdc6052f
commit
051a48885c
@ -4,7 +4,7 @@ import re
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
|
from . import providers
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
GROUP_CACHE_MAX_AGE = getattr(settings, 'DISCOURSE_GROUP_CACHE_MAX_AGE', 2 * 60 * 60) # default 2 hours
|
GROUP_CACHE_MAX_AGE = getattr(settings, 'DISCOURSE_GROUP_CACHE_MAX_AGE', 2 * 60 * 60) # default 2 hours
|
||||||
@ -19,128 +19,8 @@ class DiscourseError(Exception):
|
|||||||
return "API execution failed.\nErrors: %s\nEndpoint: %s" % (self.errors, self.endpoint)
|
return "API execution failed.\nErrors: %s\nEndpoint: %s" % (self.errors, self.endpoint)
|
||||||
|
|
||||||
|
|
||||||
# not exhaustive, only the ones we need
|
|
||||||
ENDPOINTS = {
|
|
||||||
'groups': {
|
|
||||||
'list': {
|
|
||||||
'path': "/groups/search.json",
|
|
||||||
'method': 'get',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'create': {
|
|
||||||
'path': "/admin/groups",
|
|
||||||
'method': 'post',
|
|
||||||
'args': {
|
|
||||||
'required': ['name'],
|
|
||||||
'optional': ['visible'],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'add_user': {
|
|
||||||
'path': "/admin/groups/%s/members.json",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': ['usernames'],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'remove_user': {
|
|
||||||
'path': "/admin/groups/%s/members.json",
|
|
||||||
'method': 'delete',
|
|
||||||
'args': {
|
|
||||||
'required': ['username'],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'delete': {
|
|
||||||
'path': "/admin/groups/%s.json",
|
|
||||||
'method': 'delete',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'users': {
|
|
||||||
'create': {
|
|
||||||
'path': "/users",
|
|
||||||
'method': 'post',
|
|
||||||
'args': {
|
|
||||||
'required': ['name', 'email', 'password', 'username'],
|
|
||||||
'optional': ['active'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'update': {
|
|
||||||
'path': "/users/%s.json",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': ['params'],
|
|
||||||
'optional': [],
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'get': {
|
|
||||||
'path': "/users/%s.json",
|
|
||||||
'method': 'get',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'activate': {
|
|
||||||
'path': "/admin/users/%s/activate",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'set_email': {
|
|
||||||
'path': "/users/%s/preferences/email",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': ['email'],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'suspend': {
|
|
||||||
'path': "/admin/users/%s/suspend",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': ['duration', 'reason'],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'unsuspend': {
|
|
||||||
'path': "/admin/users/%s/unsuspend",
|
|
||||||
'method': 'put',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'logout': {
|
|
||||||
'path': "/admin/users/%s/log_out",
|
|
||||||
'method': 'post',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'external': {
|
|
||||||
'path': "/users/by-external/%s.json",
|
|
||||||
'method': 'get',
|
|
||||||
'args': {
|
|
||||||
'required': [],
|
|
||||||
'optional': [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DiscourseManager:
|
class DiscourseManager:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -148,55 +28,14 @@ class DiscourseManager:
|
|||||||
SUSPEND_DAYS = 99999
|
SUSPEND_DAYS = 99999
|
||||||
SUSPEND_REASON = "Disabled by auth."
|
SUSPEND_REASON = "Disabled by auth."
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __exc(endpoint, *args, **kwargs):
|
|
||||||
params = {
|
|
||||||
'api_key': settings.DISCOURSE_API_KEY,
|
|
||||||
'api_username': settings.DISCOURSE_API_USERNAME,
|
|
||||||
}
|
|
||||||
silent = kwargs.pop('silent', False)
|
|
||||||
if args:
|
|
||||||
endpoint['parsed_url'] = endpoint['path'] % args
|
|
||||||
else:
|
|
||||||
endpoint['parsed_url'] = endpoint['path']
|
|
||||||
data = {}
|
|
||||||
for arg in endpoint['args']['required']:
|
|
||||||
data[arg] = kwargs[arg]
|
|
||||||
for arg in endpoint['args']['optional']:
|
|
||||||
if arg in kwargs:
|
|
||||||
data[arg] = kwargs[arg]
|
|
||||||
for arg in kwargs:
|
|
||||||
if arg not in endpoint['args']['required'] and arg not in endpoint['args']['optional'] and not silent:
|
|
||||||
logger.warn("Received unrecognized kwarg %s for endpoint %s" % (arg, endpoint))
|
|
||||||
r = getattr(requests, endpoint['method'])(settings.DISCOURSE_URL + endpoint['parsed_url'], headers=params,
|
|
||||||
json=data)
|
|
||||||
try:
|
|
||||||
if 'errors' in r.json() and not silent:
|
|
||||||
logger.error("Discourse execution failed.\nEndpoint: %s\nErrors: %s" % (endpoint, r.json()['errors']))
|
|
||||||
raise DiscourseError(endpoint, r.json()['errors'])
|
|
||||||
if 'success' in r.json():
|
|
||||||
if not r.json()['success'] and not silent:
|
|
||||||
raise DiscourseError(endpoint, None)
|
|
||||||
out = r.json()
|
|
||||||
except ValueError:
|
|
||||||
out = r.text
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
r.raise_for_status()
|
|
||||||
except requests.exceptions.HTTPError as e:
|
|
||||||
raise DiscourseError(endpoint, e.response.status_code)
|
|
||||||
return out
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_groups():
|
def _get_groups():
|
||||||
endpoint = ENDPOINTS['groups']['list']
|
data = providers.discourse.client.groups()
|
||||||
data = DiscourseManager.__exc(endpoint)
|
|
||||||
return [g for g in data if not g['automatic']]
|
return [g for g in data if not g['automatic']]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _create_group(name):
|
def _create_group(name):
|
||||||
endpoint = ENDPOINTS['groups']['create']
|
return providers.discourse.client.create_group(name=name[:20], visible=True)['basic_group']
|
||||||
return DiscourseManager.__exc(endpoint, name=name[:20], visible=True)['basic_group']
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_cache_group_name_key(name):
|
def _generate_cache_group_name_key(name):
|
||||||
@ -234,13 +73,11 @@ class DiscourseManager:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __add_user_to_group(g_id, username):
|
def __add_user_to_group(g_id, username):
|
||||||
endpoint = ENDPOINTS['groups']['add_user']
|
providers.discourse.client.add_group_member(g_id, username)
|
||||||
DiscourseManager.__exc(endpoint, g_id, usernames=username)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __remove_user_from_group(g_id, username):
|
def __remove_user_from_group(g_id, uid):
|
||||||
endpoint = ENDPOINTS['groups']['remove_user']
|
providers.discourse.client.delete_group_member(g_id, uid)
|
||||||
DiscourseManager.__exc(endpoint, g_id, username=username)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __generate_group_dict(names):
|
def __generate_group_dict(names):
|
||||||
@ -252,39 +89,35 @@ class DiscourseManager:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_user_groups(username):
|
def __get_user_groups(username):
|
||||||
data = DiscourseManager.__get_user(username)
|
data = DiscourseManager.__get_user(username)
|
||||||
return [g['id'] for g in data['user']['groups'] if not g['automatic']]
|
return [g['id'] for g in data['groups'] if not g['automatic']]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __user_name_to_id(name, silent=False):
|
def __user_name_to_id(name, silent=False):
|
||||||
data = DiscourseManager.__get_user(name, silent=silent)
|
data = DiscourseManager.__get_user(name)
|
||||||
return data['user']['id']
|
return data['user']['id']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_user(username, silent=False):
|
def __get_user(username, silent=False):
|
||||||
endpoint = ENDPOINTS['users']['get']
|
return providers.discourse.client.user(username)
|
||||||
return DiscourseManager.__exc(endpoint, username, silent=silent)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __activate_user(username):
|
def __activate_user(username):
|
||||||
endpoint = ENDPOINTS['users']['activate']
|
|
||||||
u_id = DiscourseManager.__user_name_to_id(username)
|
u_id = DiscourseManager.__user_name_to_id(username)
|
||||||
DiscourseManager.__exc(endpoint, u_id)
|
providers.discourse.client.activate(u_id)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __update_user(username, **kwargs):
|
def __update_user(username, **kwargs):
|
||||||
endpoint = ENDPOINTS['users']['update']
|
|
||||||
u_id = DiscourseManager.__user_name_to_id(username)
|
u_id = DiscourseManager.__user_name_to_id(username)
|
||||||
DiscourseManager.__exc(endpoint, u_id, params=kwargs)
|
providers.discourse.client.update_user(endpoint, u_id, **kwargs)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __create_user(username, email, password):
|
def __create_user(username, email, password):
|
||||||
endpoint = ENDPOINTS['users']['create']
|
providers.discourse.client.create_user(username, username, email, password)
|
||||||
DiscourseManager.__exc(endpoint, name=username, username=username, email=email, password=password, active=True)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __check_if_user_exists(username):
|
def __check_if_user_exists(username):
|
||||||
try:
|
try:
|
||||||
DiscourseManager.__user_name_to_id(username, silent=True)
|
DiscourseManager.__user_name_to_id(username)
|
||||||
return True
|
return True
|
||||||
except DiscourseError:
|
except DiscourseError:
|
||||||
return False
|
return False
|
||||||
@ -292,30 +125,26 @@ class DiscourseManager:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def __suspend_user(username):
|
def __suspend_user(username):
|
||||||
u_id = DiscourseManager.__user_name_to_id(username)
|
u_id = DiscourseManager.__user_name_to_id(username)
|
||||||
endpoint = ENDPOINTS['users']['suspend']
|
return providers.discourse.client.suspend(u_id, DiscourseManager.SUSPEND_DAYS,
|
||||||
return DiscourseManager.__exc(endpoint, u_id, duration=DiscourseManager.SUSPEND_DAYS,
|
DiscourseManager.SUSPEND_REASON)
|
||||||
reason=DiscourseManager.SUSPEND_REASON)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __unsuspend(username):
|
def __unsuspend(username):
|
||||||
u_id = DiscourseManager.__user_name_to_id(username)
|
u_id = DiscourseManager.__user_name_to_id(username)
|
||||||
endpoint = ENDPOINTS['users']['unsuspend']
|
return providers.discourse.client.unsuspend(u_id)
|
||||||
return DiscourseManager.__exc(endpoint, u_id)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __set_email(username, email):
|
def __set_email(username, email):
|
||||||
endpoint = ENDPOINTS['users']['set_email']
|
return providers.discourse.client.update_email(username, email)
|
||||||
return DiscourseManager.__exc(endpoint, username, email=email)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __logout(u_id):
|
def __logout(u_id):
|
||||||
endpoint = ENDPOINTS['users']['logout']
|
return providers.discourse.client.log_out(u_id)
|
||||||
return DiscourseManager.__exc(endpoint, u_id)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __get_user_by_external(u_id):
|
def __get_user_by_external(u_id):
|
||||||
endpoint = ENDPOINTS['users']['external']
|
data = providers.discourse.client.user_by_external_id(u_id)
|
||||||
return DiscourseManager.__exc(endpoint, u_id)
|
return data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __user_id_by_external_id(u_id):
|
def __user_id_by_external_id(u_id):
|
||||||
@ -351,7 +180,9 @@ class DiscourseManager:
|
|||||||
logger.debug("Updating discourse user %s groups to %s" % (user, groups))
|
logger.debug("Updating discourse user %s groups to %s" % (user, groups))
|
||||||
group_dict = DiscourseManager.__generate_group_dict(groups)
|
group_dict = DiscourseManager.__generate_group_dict(groups)
|
||||||
inv_group_dict = {v: k for k, v in group_dict.items()}
|
inv_group_dict = {v: k for k, v in group_dict.items()}
|
||||||
username = DiscourseManager.__get_user_by_external(user.pk)['user']['username']
|
discord_user = DiscourseManager.__get_user_by_external(user.pk)
|
||||||
|
username = discord_user['username']
|
||||||
|
uid = discord_user['id']
|
||||||
user_groups = DiscourseManager.__get_user_groups(username)
|
user_groups = DiscourseManager.__get_user_groups(username)
|
||||||
add_groups = [group_dict[x] for x in group_dict if not group_dict[x] in user_groups]
|
add_groups = [group_dict[x] for x in group_dict if not group_dict[x] in user_groups]
|
||||||
rem_groups = [x for x in user_groups if x not in inv_group_dict]
|
rem_groups = [x for x in user_groups if x not in inv_group_dict]
|
||||||
@ -364,7 +195,7 @@ class DiscourseManager:
|
|||||||
logger.info(
|
logger.info(
|
||||||
"Updating discourse user %s groups: removing %s" % (username, rem_groups))
|
"Updating discourse user %s groups: removing %s" % (username, rem_groups))
|
||||||
for g in rem_groups:
|
for g in rem_groups:
|
||||||
DiscourseManager.__remove_user_from_group(g, username)
|
DiscourseManager.__remove_user_from_group(g, uid)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def disable_user(user):
|
def disable_user(user):
|
||||||
|
@ -16,3 +16,4 @@ class DiscourseUser(models.Model):
|
|||||||
permissions = (
|
permissions = (
|
||||||
("access_discourse", u"Can access the Discourse service"),
|
("access_discourse", u"Can access the Discourse service"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
19
allianceauth/services/modules/discourse/providers.py
Normal file
19
allianceauth/services/modules/discourse/providers.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from pydiscourse import DiscourseClient
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
class DiscourseAPIClient():
|
||||||
|
_client = None
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def client(self):
|
||||||
|
if not self._client:
|
||||||
|
self._client = DiscourseClient(
|
||||||
|
settings.DISCOURSE_URL,
|
||||||
|
api_username=settings.DISCOURSE_API_USERNAME,
|
||||||
|
api_key=settings.DISCOURSE_API_KEY)
|
||||||
|
return self._client
|
||||||
|
|
||||||
|
discourse = DiscourseAPIClient()
|
@ -47,7 +47,8 @@ class DiscourseTasks:
|
|||||||
logger.debug("Updating discourse groups for user %s" % user)
|
logger.debug("Updating discourse groups for user %s" % user)
|
||||||
try:
|
try:
|
||||||
DiscourseManager.update_groups(user)
|
DiscourseManager.update_groups(user)
|
||||||
except:
|
except Exception as e:
|
||||||
|
logger.exception(e)
|
||||||
logger.warn("Discourse group sync failed for %s, retrying in 10 mins" % user)
|
logger.warn("Discourse group sync failed for %s, retrying in 10 mins" % user)
|
||||||
raise self.retry(countdown=60 * 10)
|
raise self.retry(countdown=60 * 10)
|
||||||
logger.debug("Updated user %s discourse groups." % user)
|
logger.debug("Updated user %s discourse groups." % user)
|
||||||
@ -63,3 +64,4 @@ class DiscourseTasks:
|
|||||||
def get_username(user):
|
def get_username(user):
|
||||||
from .auth_hooks import DiscourseService
|
from .auth_hooks import DiscourseService
|
||||||
return NameFormatter(DiscourseService(), user).format_name()
|
return NameFormatter(DiscourseService(), user).format_name()
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user