from __future__ import absolute_import import logging, random, string, discord from django.contrib.auth.forms import PasswordChangeForm from django.contrib.auth import update_session_auth_hash from django.apps import apps from django.conf import settings from django.contrib.auth.models import User from django.http import JsonResponse, HttpResponse from django.urls import reverse from django.utils import timezone from django.views.generic import View from django.forms import modelform_factory import minecraft_manager.forms as MCMForms from minecraft_manager.models import Player, UserSettings, Application, IP, Ticket, Warning import minecraft_manager.api.api as mcm_api import minecraft_manager.utils as mcm_utils import minecraft_manager.external.stats as mcm_stats logger = logging.getLogger(__name__) def request_allowed(request): is_authenticated = False if hasattr(request, 'user'): if hasattr(request.user, 'is_authenticated'): is_authenticated = request.user.is_authenticated password = getattr(settings, 'API_PASSWORD', None) get = request.GET post= request.POST request_password = None if 'api' in get: request_password = get['api'] elif 'api' in post: request_password = post['api'] correct_password = False if password and request_password: correct_password = request_password == password return is_authenticated or correct_password def clean(model, data): cleaned = {} for d in data: attr = d if '__' in d: attr = d.split('__')[0] if hasattr(model, attr): cleaned[d] = data[d] return cleaned def generate_password(): return "".join([random.choice(string.ascii_letters + string.digits) for idx in range(0, 20)]) class WebAPI(View): def get(self, request, keyword): get = request.GET data = {'success': False, 'message': 'API failed'} if request_allowed(request): keyword = keyword.lower() if keyword == 'log': html_global = "" html_staff = "" chats = mcm_api.get_chats(request.user.usersettings.default_timezone) if chats: for g in chats['global']: g['text'] = g['text'].replace("<", "<").replace(">", ">") if request.user.usersettings.show_timestamp_chat: html_global += "
[{0}] {1}
".format(g['date'], g['text']) else: html_global += "
{1}
".format(g['date'], g['text']) for s in chats['staff']: s['text'] = s['text'].replace("<", "<").replace(">", ">") if request.user.usersettings.show_timestamp_chat: html_staff += "
[{0}] {1}
".format(s['date'], s['text']) else: html_staff += "
{1}
".format(s['date'], s['text']) html = {'global': html_global, 'staff': html_staff} data = {'chats': chats, 'html': html} elif keyword == 'online': query = mcm_api.get_query() html = "" for p in query['players']: html += "
{0}
".format(p) html += "
" data = {'query': query, 'html': html} elif keyword == "coreprotect": if 'username' in get and 'ip' in get: user = User.objects.get(username=get['username']) if user.is_active and user.usersettings.last_ip == get['ip']: data = {'success': True, 'message': self.access_level(user)} else: data = {'success': False, 'message': 'Parameters not set'} else: data = {'message': 'Not Authorized', 'success': False} return JsonResponse(data) def post(self, request, keyword): post = request.POST data = {} if request_allowed(request): keyword = keyword.lower() if keyword == 'settings' and request.user.usersettings: for s in [a for a in dir(UserSettings) if not a.startswith('__') and not callable(getattr(UserSettings,a))]: if s in post: setattr(request.user.usersettings, s, post[s]) request.user.usersettings.save() data = {'success': True, 'message': 'User Settings saved'} elif keyword == 'password': form = PasswordChangeForm(request.user, post) if form.is_valid(): user = form.save() update_session_auth_hash(request, user) return HttpResponse("success") else: return HttpResponse(form.as_p()) elif keyword == 'alert': form = MCMForms.AlertForm(request.POST) if form.is_valid(): if mcm_api.create_alert(form.cleaned_data['message']): data = {'success': True} else: data = {'success': False, 'message': 'Could not create Alerts'} else: data = {'success': False, 'message': 'Invalid Message'} elif keyword == "discord": if 'message' in post: ping = post['ping'] if 'ping' in post else False mcm_api.discord_notification(post['message'], ping=ping) data = {'success': True, 'message': "Success", 'extra': {'message': post['message'], 'ping': ping}} else: data = {'success': False, 'message': 'No message supplied'} else: data = {'success': True, 'message': 'Model set to "{0}"'.format(keyword)} else: data = {'message': 'Not Authorized', 'success': False} return JsonResponse(data) def access_level(self, user): access = {'cpp': False, 'cpf': False, 'cpa': False} if user.has_perm('auth.coreprotect_partial'): access['cpp'] = True if user.has_perm('auth.coreprotect_full'): access['cpf'] = True if user.has_perm('auth.coreprotect_activity'): access['cpa'] = True return access class PluginAPI(View): def get(self, request, keyword): json = {'status': True, 'message': '', 'extra': ''} if request_allowed(request): get = request.GET keyword = keyword.lower() return JsonResponse(json) def post(self, request, keyword): json = {'status': True, 'message': '', 'extra': ''} if request_allowed(request): post = request.POST keyword = keyword.lower() if "application" == keyword: if Application.objects.filter(username=post['username']).exists(): application = Application.objects.get(username=post['username']) if application.accepted is not None: json['status'] = False json['message'] = "An application for {0} has already been acted on. Please contact Staff.".format( post['username']) return JsonResponse(json) else: application.accepted = None application.age = post['age'] application.player_type = post['player_type'] application.ever_banned = False if post['ever_banned'] == "no" else True application.ever_banned_explanation = post['ever_banned_explanation'] application.reference = post['reference'] application.read_rules = post['read_rules'] else: application = Application(username=post['username'], age=post['age'], player_type=post['player_type'], ever_banned=False if post['ever_banned'] == "no" else True, ever_banned_explanation= post['ever_banned_explanation'], reference=post['reference'], read_rules=post['read_rules']) application.save() if Player.objects.filter(username__iexact=post['username']).exists(): player = Player.objects.get(username__iexact=post['username']) player.application_id = application.id player.save() json['message'] = "{0}'s application was submitted.".format(application.username) json['extra'] = application.id msg = mcm_utils.build_application(application) mcm_api.discord_mcm(message='New Application!', embeds=msg) elif "application_action" == keyword: if Application.objects.filter(id=post['application_id']).exists(): application = Application.objects.get(id=post['application_id']) if application.accepted is not None: json['status'] = False json['message'] = "Application was already {0}.".format( "accepted" if post['action'] == "True" else "denied") else: application.accepted = True if post['action'] == "True" else False application.save() json['message'] = "Application was successfully {0}.".format( "accepted" if post['action'] == "True" else "denied") mcm_api.discord_mcm("{0}'s application (#{1}) was {2} by {3}".format(application.username, application.id, "accepted" if post['action'] == "True" else "denied", post['username'])) mcm_api.plugin("accept" if post['action'] == "True" else "deny", application.username) else: json['status'] = False json['message'] = "No application found." return JsonResponse(json) elif "application_clear" == keyword: if Application.objects.filter(id=post['application_id']).exists(): application = Application.objects.get(id=post['application_id']) if application.accepted is True: json['status'] = False json['message'] = "An accepted application can't be cleared." else: application.accepted = None application.save() json['message'] = "Application was successfully cleared." else: json['status'] = False json['message'] = "No application found." return JsonResponse(json) elif "login" == keyword: player = Player.objects.filter(uuid=post['uuid']).exists() new_player = False if not player: player = Player(uuid=post['uuid'], username=post['username']) new_player = True player.first_seen = timezone.now().strftime("%Y-%m-%d") test_app = Application.objects.filter(username__iexact=post['username']).exists() if test_app: test_app = Application.objects.get(username__iexact=post['username']) player.application = test_app player.save() else: player = Player.objects.get(uuid=post['uuid']) if player.username != post['username']: user = User.objects.filter(username__iexact=player.username).exists() if user: user = User.objects.get(username__iexact=player.username) user.username = post['username'].lower() user.save() mcm_api.create_alert("Name Change: {0} to {1}.".format(player.username, post['username'])) player.username = post['username'] if not player.application: test_app = Application.objects.filter(username__iexact=post['username']).exists() if test_app: test_app = Application.objects.get(username__iexact=player.username) player.application = test_app player.save() test_ip = IP.objects.filter(ip=post['ip'], player=player).exists() if not test_ip: ip = IP(ip=post['ip'], player=player) ip.save() else: ip = IP.objects.get(ip=post['ip'], player=player) player = Player.objects.get(uuid=post['uuid']) player.last_seen = timezone.now().strftime("%Y-%m-%d") player.save() if new_player and ip.associated: for assoc in ip.associated: if assoc.uuid is not player.uuid and assoc.is_banned: mcm_api.plugin("staff", "Server {0}'s IP matches the banned player {1}".format(player.username, assoc.username)) mcm_api.discord_notification("{0}'s IP matches the banned player {1}".format(player.username, assoc.username), ping=True) json['status'] = True json['message'] = "Updated {0}".format(post['username']) elif "register" == keyword: player = Player.objects.get(uuid=post['uuid']) if player.auth_user: json['status'] = False json['message'] = "You are already registered. To change your password, contact an Admin." else: password = generate_password() user = User.objects.create_user(username=player.username.lower(), password=password) user.save() player.auth_user = user player.save() json['message'] = password elif "ticket" == keyword: player = Player.objects.get(uuid=post['uuid']) try: ticket = Ticket(player=player, message=post['message'], x=post['x'], y=post['y'], z=post['z'], world=post['world']) ticket.save() json['message'] = "Ticket submitted." link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/ticket', ticket.id)) msg = mcm_utils.build_ticket(ticket, link) json['extra'] = {'id': ticket.id, 'link': link} mcm_api.discord_mcm(embeds=msg, ping=True) except: json['status'] = False json['message'] = "Error while submitting ticket." elif "warning" == keyword: player = Player.objects.get(uuid=post['player']) staff = Player.objects.get(uuid=post['staff']) try: warning = Warning(player=player, message=post['message'], severity=post['severity'], staff=staff.auth_user) warning.save() json['message'] = "Warning issued." link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard/warning', warning.id)) msg = mcm_utils.build_warning(warning, link) mcm_api.discord_mcm(embeds=msg) except Exception as ex: json['status'] = False json['message'] = "Error while issuing warning." return JsonResponse(json) class FormAPI(View): def get(self, request, request_model): html = "" if request_allowed(request): get = request.GET model = None for m in apps.get_app_config('minecraft_manager').get_models(): if m._meta.model_name.upper() == request_model.upper(): model = m break if model: form = None for modelform in MCMForms.__all__(): if modelform.Meta.model == model: form = modelform() break if not form: form = modelform_factory(model, exclude=('id',))() if 'as' in get: html = getattr(form, 'as_{0}'.format(get['as']))() else: html = form.as_p() return HttpResponse(html) def post(self, request, request_model): html = "" if request_allowed(request): post = request.POST model = None for m in apps.get_app_config('minecraft_manager').get_models(): if m._meta.model_name.upper() == request_model.upper(): model = m break if model: form = None for modelform in MCMForms.__all__(): if modelform.Meta.model == model: form = modelform(post) break if not form: form = modelform_factory(model, exclude=('id',))(post) if form.is_valid(): form.save() html = "Saved" else: if 'as' in post: html = getattr(form, 'as_{0}'.format(post['as']))() else: html = form.as_p() return HttpResponse(html) class ModelAPI(View): def get(self, request, request_model): json = [] if request_allowed(request): get = request.GET model = None for m in apps.get_app_config('minecraft_manager').get_models(): if m._meta.model_name.upper() == request_model.upper(): model = m break if model: keywords = clean(model, get) objects = model.objects.filter(**keywords).values() json = [] for value in objects: try: link = "{}".format(mcm_utils.url_path(settings.MCM_BASE_LINK, 'dashboard', request_model, value['id'])) value['link'] = link except: pass json.append(value) return JsonResponse(json, safe=False) def post(self, request, request_model): pass class StatsAPI(View): def get(self, request): json = [] if request_allowed(request): get = request.GET if 'stat' in get: if 'uuid' in get: json = mcm_stats.one_single(get['uuid'], get['stat']) elif 'username' in get and Player.objects.filter(username__iexact=get['username']).exists(): uuid = Player.objects.get(username__iexact=get['username']).uuid json = mcm_stats.one_single(uuid, get['stat']) else: json = mcm_stats.top_ten_stat(get['stat']) elif 'uuid' in get: json = mcm_stats.top_ten_player(get['uuid']) elif 'username' in get: uuid = Player.objects.get(username__iexact=get['username']).uuid json = mcm_stats.top_ten_player(uuid) return JsonResponse(json, safe=False) def post(self, request): pass