Beispiel #1
0
def audit_forums():

    import common.logger as _logger
    import common.credentials.database as _database
    import common.credentials.forums as _forumcreds
    import common.ldaphelpers as _ldaphelpers
    import common.request_esi
    from tri_core.common.testing import vg_blues, vg_alliances
    from common.graphite import sendmetric
    from collections import defaultdict
    import json
    import urllib
    import html
    import MySQLdb as mysql
    import re

    import hashlib
    from passlib.hash import ldap_salted_sha1
    import uuid

    _logger.log('[' + __name__ + '] auditing forums',_logger.LogLevel.INFO)

    try:
        sql_conn_core = mysql.connect(
            database=_database.DB_DATABASE,
            user=_database.DB_USERNAME,
            password=_database.DB_PASSWORD,
            host=_database.DB_HOST)
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
        return False

    try:
        sql_conn_forum = mysql.connect(
            database=_forumcreds.mysql_db,
            user=_forumcreds.mysql_user,
            password=_forumcreds.mysql_pass,
            host=_forumcreds.mysql_host)
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
        return False

    # get everything from the permissions table

    cursor = sql_conn_core.cursor()
    query = 'SELECT allianceID,forum FROM Permissions'
    forum_mappings = dict()

    try:
        cursor.execute(query)
        rows = cursor.fetchall()
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
        return False
    finally:
        cursor.close()
        sql_conn_core.close()

    # this maps the alliance id to the primary forum group

    for row in rows:
        forum_mappings[row[0]] = row[1]

    # handle vanguard blues as well
    # these are a special case of limited access

    for alliance in vg_blues():
        forum_mappings[alliance] = 73

    # get all the forum users and stuff them into a dict for later processing

    cursor = sql_conn_forum.cursor()
    query = 'SELECT name, member_group_id, mgroup_others, ip_address, pp_main_photo, pp_thumb_photo FROM core_members'
    try:
        cursor.execute(query)
        rows = cursor.fetchall()
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql forum error: ' + str(err), _logger.LogLevel.ERROR)
        return False

    total_users = len(rows)
    _logger.log('[' + __name__ + '] forum users: {0}'.format(total_users), _logger.LogLevel.INFO)
    sendmetric(__name__, 'forums', 'statistics', 'total_users', total_users)

    users = dict()
    orphan = 0

    # special forum users that are not to be audited
    special = [ 'Admin', 'Sovereign' ]

    for charname, primary_group, secondary_string, last_ip,  pp_main_photo, pp_thumb_photo in rows:

        # dont work on these
        if charname in special:
            continue

        if charname == '' or charname == None:
            continue

        # recover user information
        users[charname] = dict()
        users[charname]['charname'] = charname
        users[charname]['doomheim'] = False
        users[charname]['primary'] = primary_group
        users[charname]['last_ip'] = last_ip
        users[charname]['pp_main_photo'] = pp_main_photo
        users[charname]['pp_thumb_photo'] = pp_thumb_photo

        # convert the comma separated list of groups to an array of integers

        secondaries = []
        for item in secondary_string.split(','):
            if not item == '' and not item == ' ':
                secondaries.append(int(item))
        users[charname]['secondary'] = secondaries

        cn, _ = _ldaphelpers.ldap_normalize_charname(charname)

        # match up against ldap data
        dn = 'ou=People,dc=triumvirate,dc=rocks'
        filterstr='cn={0}'.format(cn)
        attributes = ['authGroup', 'accountStatus', 'uid', 'alliance', 'corporation' ]
        code, result = _ldaphelpers.ldap_search(__name__, dn, filterstr, attributes)
        if code == False:
            _logger.log('[' + __name__ + '] ldap error: {0}'.format(result), _logger.LogLevel.ERROR)
            return

        if result == None:
            # nothing in ldap.
            # you WILL be dealt with later!
            orphan += 1
            users[charname]['charid'] = None
            users[charname]['alliance'] = None
            users[charname]['authgroups'] = []
            users[charname]['accountstatus'] = None
            users[charname]['corporation'] = None
        else:
            (dn, info), = result.items()

            alliance = info.get('alliance')
            corporation = info.get('corporation')

            if alliance is not None:
                alliance = int(alliance)
            if corporation is not None:
                corporation = int(corporation)

            users[charname]['alliance'] = alliance
            users[charname]['corporation'] = corporation

            users[charname]['charid'] = int( info['uid'] )
            users[charname]['accountstatus'] = info['accountStatus']
            users[charname]['authgroups'] = info['authGroup']

    # postprocessing

    _logger.log('[' + __name__ + '] forum users with no LDAP entry: {0}'.format(orphan),_logger.LogLevel.INFO)

    # map each user to a charID given that the username is exactly
    # a character name

    really_orphan = 0

    for charname in users:
        user = users[charname]
        primary = user['primary']

        if user['charid'] == None:
            # need to map the forum username to a character id
            query = { 'categories': 'character', 'language': 'en-us', 'search': charname, 'strict': 'true' }
            query = urllib.parse.urlencode(query)
            esi_url = 'search/?' + query
            code, result = common.request_esi.esi(__name__, esi_url, 'get')
            _logger.log('[' + __name__ + '] /search output: {}'.format(result), _logger.LogLevel.DEBUG)

            if not code == 200:
                _logger.log('[' + __name__ + '] error searching for user {0}: {1}'.format(charname, result['error']),_logger.LogLevel.INFO)
                continue
            if len(result) == 0:
                really_orphan += 1
                users[charname]['doomheim'] = True
            if len(result) == 1:

                # make a stub ldap entry for them

                charid = result['character'][0]
                users[charname]['charid'] = charid

                # the forum stub exists only to pin
                _ldaphelpers.ldap_create_stub(charname, charid, authgroups=['public', 'forum_stub'])

    _logger.log('[' + __name__ + '] forum users who have biomassed: {0}'.format(really_orphan),_logger.LogLevel.INFO)

    # at this point every forum user has been mapped to their character id either via ldap or esi /search
    # work through each user and determine if they are correctly setup on the forums

    non_tri = 0
    for charname in users:

        charid = users[charname]['charid']

        if users[charname]['doomheim'] == True:
            forumpurge(charname)
            continue

        authgroups = users[charname]['authgroups']
        alliance = users[charname]['alliance']
        corporation = users[charname]['corporation']
        primary_group = users[charname]['primary']
        secondary_groups = users[charname]['secondary']
        forum_lastip = users[charname]['last_ip']

        if users[charname]['pp_main_photo'] is None:
        # set forum portrait to their in-game avatar if the user doesn't have one

            esi_url = 'characters/{0}/portrait/'.format(charid)
            code, result = common.request_esi.esi(__name__, esi_url, 'get')
            _logger.log('[' + __name__ + '] /characters portrait output: {}'.format(result), _logger.LogLevel.DEBUG)

            if code == 200:

                portrait = result['px128x128'].replace("http", "https")
                query = 'UPDATE core_members SET pp_main_photo=%s, pp_thumb_photo=%s, pp_photo_type="custom" WHERE name = %s'
                try:
                    cursor.execute(query, (portrait, portrait, charname,))
                    sql_conn_forum.commit()
                except Exception as err:
                    _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
                    return

            else:
                # something broke severely
                _logger.log('[' + __name__ + '] /characters portrait API error {0}: {1}'.format(code, result['error']), _logger.LogLevel.ERROR)


        ## start doing checks

        # only people in tri get anything other than public access on the tri forums
        # forum public/unprivileged groupid: 2

        vanguard = forum_mappings.keys()

        if alliance not in vanguard and primary_group != 2:

            # this char is not in a vanguard alliance or blue but has non-public forum access
            forumpurge(charname)

            # log

            _logger.securitylog(__name__, 'forum demotion', charid=charid, charname=charname, ipaddress=forum_lastip)
            msg = 'non-vanguard character with privileged access demoted. charname: {0} ({1}), alliance: {2}, primary group: {3}, secondary group(s): {4}, last ip: {5}'.format(
                charname, charid, alliance, primary_group, secondary_groups, forum_lastip
            )
            _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.WARNING)
            non_tri += 1

        if alliance in vanguard:

            correct_primary = forum_mappings[alliance]
            correct_secondaries = []
            # construct the list of correct secondary groups

            for authgroup in authgroups:
                mapping = authgroup_map(authgroup)
                if not mapping == None:
                    correct_secondaries.append(mapping)

            # map any custom corp groups
            corpgroup = corp_map(corporation)
            if not corpgroup == None:
                correct_secondaries.append(corpgroup)

            ## deal with secondary groups

            # remove secondaries
            msg = 'char {0} current secondaries: {1} correct secondaries: {2}'.format(charname, secondary_groups, correct_secondaries)
            _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.DEBUG)

            secondaries_to_remove = list( set(secondary_groups) - set(correct_secondaries) )

            # ips forum likes a comma separated list of secondaries
            if len(secondaries_to_remove) > 0:
                change = ''
                for group in correct_secondaries:
                    change = change + str(group) + ','
                change = change[:-1] # peel off trailing comma

                query = 'UPDATE core_members SET mgroup_others=%s WHERE name=%s'
                try:
                    cursor.execute(query, (change, charname,))
                    sql_conn_forum.commit()
                    msg = 'removed secondary group(s) {0} from {1}'.format(secondaries_to_remove, charname)
                    _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.INFO)
                except Exception as err:
                    _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
                    return False

            secondaries_to_add = list( set(correct_secondaries) - set(secondary_groups) )

            # ips forum likes a comma separated list of secondaries
            if len(secondaries_to_add) > 0:
                change = ''
                for group in correct_secondaries:
                    change = change + str(group) + ','
                change = change[:-1] # peel off trailing comma

                query = 'UPDATE core_members SET mgroup_others=%s WHERE name=%s'
                try:
                    cursor.execute(query, (change, charname,))
                    sql_conn_forum.commit()
                    msg = 'added secondary group(s) {0} to {1}'.format(secondaries_to_add, charname)
                    _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.INFO)
                except Exception as err:
                    _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
                    return False

            if not correct_primary == primary_group:
                query = 'UPDATE core_members SET member_group_id=%s WHERE name=%s'
                try:
                    cursor.execute(query, (correct_primary, charname,))
                    sql_conn_forum.commit()
                    _logger.log('[' + __name__ + '] adjusted primary forum group of {0} to {1}'.format(charname, correct_primary),_logger.LogLevel.INFO)
                except Exception as err:
                    _logger.log('[' + __name__ + '] mysql error: ' + str(err), _logger.LogLevel.ERROR)
                    return False
    cursor.close()
    sql_conn_forum.close()
    _logger.log('[' + __name__ + '] non-vanguard forum users reset: {0}'.format(non_tri),_logger.LogLevel.INFO)
Beispiel #2
0
def core_corpsupers():
    dn = 'ou=People,dc=triumvirate,dc=rocks'

    # logging

    logger = getlogger('core.corpsupers')

    ipaddress = request.args.get('log_ip')
    if ipaddress is None:
        ipaddress = request.headers['X-Real-Ip']

    charid = request.args.get('charid')

    if charid is None:
        error = 'need a charid to authenticate with'
        js = json.dumps({'error': error})
        resp = Response(js, status=405, mimetype='application/json')
        return resp

    securitylog('supers audit', ipaddress=ipaddress, charid=charid)

    # check for auth groups

    allowed_roles = ['Director', 'Personnel_Manager']
    roles = check_role(charid, allowed_roles)

    if not roles:
        return Response(json.dumps({'error': "forbidden"}),
                        status=403,
                        mimetype='application/json')

    # get corp

    code, result = _ldaphelpers.ldap_search(__name__, dn,
                                            '(uid={})'.format(charid),
                                            ['corporation'])

    if code == 'error':
        error = 'unable to check auth groups roles for {0}: ({1}) {2}'.format(
            charid, code, result)
        logger.error(msg)
        js = json.dumps({'error': error})
        resp = Response(js, status=500, mimetype='application/json')
        return resp

    if result == None:
        msg = 'charid {0} not in ldap'.format(charid)
        logger.error(msg)
        js = json.dumps({'error': msg})
        return Response(js, status=404, mimetype='application/json')

    (_, result), = result.items()

    # get super pilots

    filterstr = '(&(esiAccessToken=*)(corporation={0}))'.format(
        result['corporation'])
    attrlist = ['uid', 'corporation', 'characterName', 'altOf']

    code_supers, result_supers = _ldaphelpers.ldap_search(
        __name__, dn, filterstr, attrlist)

    supers = dict()

    with ThreadPoolExecutor(25) as executor:
        futures = {
            executor.submit(audit_pilot, result_supers[cn]): cn
            for cn in result_supers
        }
        for future in as_completed(futures):
            data = future.result()

            try:
                supers.update(data)
            except Exception as err:
                msg = 'super audit for failed: {0}'.format(err)
                logger.error(msg)

    supers_cleaned = {}

    for key in supers:
        supers_cleaned[supers[key]['item_id']] = supers[key]

    js = json.dumps(supers_cleaned)
    return Response(js, status=200, mimetype='application/json')
Beispiel #3
0
def ts3_validate_users(ts3conn):

    import ldap
    import common.ldaphelpers as _ldaphelpers
    import common.credentials.ldap as _ldap
    import common.credentials.ts3 as _ts3
    import common.logger as _logger
    import time
    import ts3

    from tri_core.common.tsgroups import teamspeak_groups


    try:
        ldap_conn = ldap.initialize(_ldap.ldap_host, bytes_mode=False)
        ldap_conn.simple_bind_s(_ldap.admin_dn, _ldap.admin_dn_password)
    except ldap.LDAPError as error:
        msg = 'LDAP connection error: {}'.format(error)
        _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.ERROR)


    # generic purge of TS from non-blue ldap users
    # only matches people who should not have a TS identity

    dn = 'ou=People,dc=triumvirate,dc=rocks'
    filterstr = '(&(!(accountStatus=blue))(teamspeakuid=*))'
    attributes = ['characterName', 'uid', 'esiAccessToken' ]
    code, result = _ldaphelpers.ldap_search(__name__, dn, filterstr, attributes)

    if code == False:
        return

    if result == None:
        # nobody. no problem.
        pass
    else:
        result_count = len(result)
        # some ppl are banned/whatever and have a TS identity!
        msg = '{0} unauthorized users with a TS identity'.format(result_count)
        _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.WARNING)

        for user in result.keys():

            charid = int( result[user]['uid'] )

            # decouple their TS identities from their LDAP entry

            mod_attrs = []
            mod_attrs.append((ldap.MOD_DELETE, 'teamspeakdbid', None ))
            mod_attrs.append((ldap.MOD_DELETE, 'teamspeakuid', None ))
            try:
                ldap_conn.modify_s(user, mod_attrs)
                msg = 'purged TS identity from unauthorized user: {}'.format(user)
                _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.INFO)
            except ldap.LDAPError as error:
                _logger.log('[' + __name__ + '] unable to purge TS entries for {0}: {1}'.format(dn, error),_logger.LogLevel.ERROR)

    # validate ts3 online users

    # skip these users
    skip = [ 'ServerQuery Guest', 'sovereign' ]

    # it turns out clientdblist() are just users who are online, rather than ALL users or something
    # this does not audit users-within-groups apparently
    try:
        resp = ts3conn.clientdblist()
    except ts3.query.TS3QueryError as err:
        _logger.log('[' + __name__ + '] ts3 error: {0}'.format(err),_logger.LogLevel.ERROR)
        return

    clients = []

    for user in resp.parsed:
        serviceuser = user['client_nickname']
        if serviceuser in skip:
            # we don't want to waste time on internal users
            continue
        clients.append(int(user['cldbid']))

    # get the rest of the clients out of the various groups, then de-dupe

    try:
        resp = ts3conn.servergrouplist()
        groups = resp.parsed
    except ts3.query.TS3QueryError as err:
        _logger.log('[' + __name__ + '] ts3 error: {0}'.format(err),_logger.LogLevel.ERROR)
        return

    for group in groups:
        # dig out all the users in each group
        groupid = int(group['sgid'])

        skip = [ 8 ]

        if groupid in skip:
            # TS does NOT like looking into default groups (?)
            continue
        try:
            resp = ts3conn.servergroupclientlist(sgid=group['sgid'])
            result = resp.parsed
        except ts3.query.TS3QueryError as err:
            _logger.log('[' + __name__ + '] ts3 error: {0}'.format(err),_logger.LogLevel.ERROR)
            return
        for client in result:
            clients.append(int(client['cldbid']))

    # deduplicate using set and filter out dbids that have to be skipped
    # server query only, basically

    skip = [ 1 ]

    clients = set(clients) - set(skip)
    clients = list(clients)

    clientcount = len(clients)
    _logger.log('[' + __name__ + '] distinct ts3 client identities: {0}'.format(clientcount),_logger.LogLevel.DEBUG)

    # get the current TS3 client list
    try:
        resp = ts3conn.clientlist()
        live_clients = resp.parsed
    except ts3.query.TS3QueryError as err:
        _logger.log('[' + __name__ + '] unable to fetch TS client list: {0}'.format(err),_logger.LogLevel.WARNING)


    # start validating clients, online or otherwise
    for ts_dbid in clients:

        # this should never fail since the dbid we have is fed from the ts3 client list upstream

        try:
            resp = ts3conn.clientdbinfo(cldbid=ts_dbid)
            user = resp.parsed
        except ts3.query.TS3QueryError as err:
            _logger.log('[' + __name__ + '] ts3 (uid: {0}) error: "{1}"'.format(ts_dbid, err),_logger.LogLevel.WARNING)
            return

        user_nick = user[0]['client_nickname']
        _logger.log('[' + __name__ + '] Validating ts3 user "{0}"'.format(user_nick),_logger.LogLevel.DEBUG)

        user_lastip = user[0]['client_lastip']
        user_lastconn = int(user[0]['client_lastconnected'])
        user_conns = int(user[0]['client_totalconnections'])
        user_created = int(user[0]['client_created'])

        token = None # for token checks later
        orphan = False # for detached TS registrations
        kicked = False # users can be kicked in a few spots prior to final purge spot

        # we explicitly check only for blue users that have this dbid.
        # this means people with non-blue status (public, banned) lose their TS

        dn = 'ou=People,dc=triumvirate,dc=rocks'
        filterstr='(&(accountStatus=blue)(teamspeakdbid={}))'.format(ts_dbid)
        attrlist=['characterName', 'uid', 'esiAccessToken' ]

        code, result = _ldaphelpers.ldap_search(__name__, dn, filterstr, attributes)

        if code == False:
            return

        if result == None:
            # this can happen if the TS user has an entry in the TS db but nothing in ldap
            # this user will be dealt with downstream
            _logger.log('[' + __name__ + '] ts3 orphan dbid: {0}'.format(ts_dbid),_logger.LogLevel.INFO)
            registered_username = None
            orphan = True


        elif len(result) == 1:
            # the dbid is matched to an single ldap user
            (dn, info), = result.items()
            charname = info['characterName']
            charid = int( info['uid'] )
            registered_username = charname

            try:
                token = info['esiAccessToken']
            except Exception as e:
                token = None

            _logger.log('[' + __name__ + '] charid {0} validated non-orphan'.format(charid),_logger.LogLevel.DEBUG)

            # do a TS group validate

            code, result = teamspeak_groups(charid)

            if code == False:
                msg = 'unable to setup teamspeak groups for {0}: {1}'.format(charname, result)
                _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.ERROR)
                continue

        elif len(result) > 1:
            # multiple TS registrations. naughty.

            _logger.log('[' + __name__ + '] multiple unique TS identities attached to accounts: {0}'.format(result),_logger.LogLevel.WARNING)

            orphan = True
            # boot from TS...
            reason = 'Please re-register your TS on CORE. On only one character.'
            try:
                resp = ts3conn.clientkick(reasonid=5, reasonmsg=reason, clid=clid)
                _logger.log('[' + __name__ + '] ts3 user {0} kicked from server: active orphan'.format(user_nick),_logger.LogLevel.WARNING)
                _logger.log('[' + __name__ + '] TS db: "{0}", client: "{1}"'.format(registered_username,client_username),_logger.LogLevel.DEBUG)
                kicked = True
                _logger.securitylog(__name__, 'ts3 duplicate character', charname=registered_username, date=user_lastconn, ipaddress=user_lastip)
            except ts3.query.TS3QueryError as err:
                _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.ERROR)

            # ...break the ldap <--> TS link on each character
            for user in result.keys():

                charid = int( result[user]['uid'] )

                mod_attrs = []
                mod_attrs.append((ldap.MOD_DELETE, 'teamspeakdbid', None ))
                mod_attrs.append((ldap.MOD_DELETE, 'teamspeakuid', None ))
                try:
                    ldap_conn.modify_s(user, mod_attrs)
                    msg = 'purged TS identity from duplicate user: {}'.format(user)
                    _logger.log('[' + __name__ + '] {}'.format(msg),_logger.LogLevel.INFO)
                except ldap.LDAPError as error:
                    _logger.log('[' + __name__ + '] unable to purge TS entries for {0}: {1}'.format(dn, error),_logger.LogLevel.ERROR)


        # if the user is online, we want to make sure that the username matches
        # what is in the teamspeak table

        for client in live_clients:
            clid = client['clid']
            cldbid = int(client['client_database_id'])
            client_username = client['client_nickname']


            if orphan is True and cldbid == ts_dbid:
                # a registered TS user needs to have an ESI token on their LDAP
                reason = 'You are no longer registered on CORE'
                continue
                try:
                    resp = ts3conn.clientkick(reasonid=5, reasonmsg=reason, clid=clid)
                    _logger.log('[' + __name__ + '] ts3 user {0} kicked from server: no active registration'.format(user_nick),_logger.LogLevel.WARNING)
                    _logger.log('[' + __name__ + '] TS db: "{0}", client: "{1}"'.format(registered_username,client_username),_logger.LogLevel.DEBUG)
                    kicked = True
                    _logger.securitylog(__name__, 'ts3 without ESI', charname=registered_username, date=user_lastconn, ipaddress=user_lastip)
                except ts3.query.TS3QueryError as err:
                    _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.ERROR)

            if not client_username or not registered_username:
                continue

            if client_username.lower() == registered_username.lower() and token == None and kicked == False:
                # a registered TS user needs to have an ESI token on their LDAP
                reason = 'Please login to CORE. Your token has become invalid.'
                try:
                    resp = ts3conn.clientkick(reasonid=5, reasonmsg=reason, clid=clid)
                    _logger.log('[' + __name__ + '] ts3 user {0} kicked from server: no esi token'.format(user_nick),_logger.LogLevel.WARNING)
                    _logger.log('[' + __name__ + '] TS db: "{0}", client: "{1}"'.format(registered_username,client_username),_logger.LogLevel.DEBUG)
                    kicked = True
                    _logger.securitylog(__name__, 'ts3 without ESI', charname=registered_username, date=user_lastconn, ipaddress=user_lastip)
                except ts3.query.TS3QueryError as err:
                    _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.ERROR)

            if cldbid == ts_dbid and client_username != registered_username and kicked == False:
                # online user has a username that does not match records.
                # "encourage" fixing this.
                reason = 'Please use your main character name as your teamspeak nickname, without any tags'
                try:
                    resp = ts3conn.clientkick(reasonid=5, reasonmsg=reason, clid=clid)
                    _logger.log('[' + __name__ + '] ts3 user {0} kicked from server: mismatch'.format(user_nick),_logger.LogLevel.WARNING)
                    _logger.log('[' + __name__ + '] TS db: "{0}", client: "{1}"'.format(registered_username,client_username),_logger.LogLevel.DEBUG)
                    _logger.securitylog(__name__, 'ts3 name mismatch', charname=registered_username, date=user_lastconn, ipaddress=user_lastip, detail='wrong name: {0}'.format(client_username))
                    kicked = True
                except ts3.query.TS3QueryError as err:
                    _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.ERROR)

        # handle orphan TS users

        if orphan == True:
            # log the shit out of the orphan user

            lastconnected = time.gmtime(user_lastconn)
            lastconnected_iso = time.strftime("%Y-%m-%dT%H:%M:%S", lastconnected)
            created = time.gmtime(user_created)
            created_iso = time.strftime("%Y-%m-%dT%H:%M:%S", created)
            _logger.log('[' + __name__ + '] Orphan ts3 user: {0}'.format(user_nick), _logger.LogLevel.WARNING)
            _logger.log('[' + __name__ + '] User {0} created: {1}, last login: {2}, last ip: {3}, total connections: {4}'.format(
                user_nick,created_iso,lastconnected_iso,user_lastip,user_conns),
                _logger.LogLevel.WARNING
            )


            # first kick from the server if they are on, asking them to re-register
            # to do that i need the client id, which is not the client db id, because
            # of f*****g course it isn't

            for client in live_clients:
                clid = client['clid']
                cldbid = client['client_database_id']
                if cldbid == ts_dbid and kicked == False:
                    try:
                        reason = 'You are detached from CORE. Please configure services.'
                        resp = ts3conn.clientkick(reasonid=5, reasonmsg=reason, clid=clid)
                        _logger.log('[' + __name__ + '] ts3 user {0} kicked from server (orphan user)'.format(user_nick),_logger.LogLevel.WARNING)
                        _logger.securitylog(__name__, 'orphan ts3 user', charname=user_nick, date=user_lastconn, ipaddress=user_lastip)
                    except ts3.query.TS3QueryError as err:
                        _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.WARNING)

            # now remove the client from the ts3 database

            try:
                resp = ts3conn.clientdbdelete(cldbid=ts_dbid)
                _logger.log('[' + __name__ + '] ts3 user {0} removed'.format(user_nick),_logger.LogLevel.WARNING)
            except ts3.query.TS3QueryError as err:
                _logger.log('[' + __name__ + '] ts3 error: "{0}"'.format(err),_logger.LogLevel.WARNING)

            # client removed. gg.
        else:
            # nothing to do. yer good.
            pass
Beispiel #4
0
def maint_tokens():

    logger = getlogger('maint.tokens')

    # do esi status check first

    request_url = 'status'
    code, result = common.request_esi.esi(__name__, request_url, method='get', version='v1')

    if not code == 200:
        error = 'ESI offline'
        logger.error(error)
        return

    # grab each token from ldap

    dn = 'ou=People,dc=triumvirate,dc=rocks'
    filterstr = '(|(esiRefreshToken=*)(discordRefreshToken=*))'
    attrlist = ['esiRefreshToken', 'esiAccessTokenExpires', 'discordRefreshToken', 'discordAccessTokenExpires', 'discorduid', 'esiScope', 'uid', 'corporationRole']

    code, result = _ldaphelpers.ldap_search(__name__, dn, filterstr, attrlist)

    if code == False:
        return

    msg = 'ldap users with defined refresh tokens: {0}'.format(len(result))
    logger.info(msg)

    evetokens = dict()
    discordtokens = dict()

    for dn, info in result.items():

        eve_rtoken = info.get('esiRefreshToken')
        discord_rtoken = info.get('discordRefreshToken')
        evetokens[dn] = dict()
        discordtokens[dn] = dict()

        if eve_rtoken is not None:

            evetokens[dn]['rtoken'] = eve_rtoken
            evetokens[dn]['uid'] = int( info.get('uid') )
            evetokens[dn]['scopes'] = info.get('esiScope')
            evetokens[dn]['roles'] = info.get('corporationRole')
            evetokens[dn]['expires'] = info.get('esiAccessTokenExpires')

        if discord_rtoken is not None:

            discordtokens[dn]['rtoken'] = discord_rtoken
            discordtokens[dn]['expires'] = float( info.get('discordAccessTokenExpires') )
            discordtokens[dn]['uid'] = int( info.get('uid') )

            if info.get('discorduid'):
                discordtokens[dn]['discorduid'] = int( info.get('discorduid') )

    # dump the tokens into a pool to bulk manage

    with ThreadPoolExecutor(40) as executor:
        futures = { executor.submit(tokenthings, dn, evetokens[dn], discordtokens[dn]): dn for dn in evetokens.keys() }
        for future in as_completed(futures):
            data = future.result()
Beispiel #5
0
def audit_corp(charid, corp_id):

    dn = 'ou=People,dc=triumvirate,dc=rocks'

    corp_result = {}

    corp_result['id'] = corp_id

    request_url = 'corporations/{}/'.format(corp_id)
    esi_corporation_code, esi_corporation_result = common.request_esi.esi(
        __name__, request_url, method='get')

    if not esi_corporation_code == 200:
        # something broke severely
        _logger.log(
            '[' + __name__ + '] corporation API error {0}: {1}'.format(
                esi_corporation_code, esi_corporation_result['error']),
            _logger.LogLevel.ERROR)
        return corp_result

    corp_result['name'] = esi_corporation_result['name']
    corp_result['members'] = esi_corporation_result['member_count']

    code_mains, result_mains = _ldaphelpers.ldap_search(
        __name__, dn, '(&(corporation={0})(!(altOf=*)))'.format(corp_id),
        ['uid'])

    if code_mains == 'error':
        error = 'unable to count main ldap users {0}: ({1}) {2}'.format(
            charid, code_mains, result_mains)
        _logger.log('[' + __name__ + ']' + error, _logger.LogLevel.ERROR)
        return corp_result

    code_registered, result_registered = _ldaphelpers.ldap_search(
        __name__, dn, 'corporation={0}'.format(corp_id), ['uid'])

    if code_registered == 'error':
        error = 'unable to count registered ldap users {0}: ({1}) {2}'\
            .format(charid, code_registered, result_registered)
        _logger.log('[' + __name__ + ']' + error, _logger.LogLevel.ERROR)
        return corp_result

    code_tokens, result_tokens = _ldaphelpers.ldap_search(
        __name__, dn, '(&(corporation={0})(esiAccessToken=*))'.format(corp_id),
        ['uid'])

    if code_tokens == 'error':
        error = 'unable to count token\'d ldap users {0}: ({1}) {2}'.format(
            charid, code_tokens, result_tokens)
        _logger.log('[' + __name__ + ']' + error, _logger.LogLevel.ERROR)
        return corp_result

    if result_tokens is None:
        corp_result['tokens'] = 0
    else:
        corp_result['tokens'] = len(result_tokens)

    if result_registered is None:
        corp_result['registered'] = 0
    else:
        corp_result['registered'] = len(result_registered)

    if result_mains is None:
        corp_result['mains'] = 0
    else:
        corp_result['mains'] = len(result_mains)

    return corp_result
Beispiel #6
0
def core_audit_alliance(allianceid):

    # logging

    ipaddress = request.args.get('log_ip')
    if ipaddress is None:
        ipaddress = request.headers['X-Real-Ip']

    charid = request.args.get('charid')

    if charid is None:
        error = 'need a charid to authenticate with'
        js = json.dumps({'error': error})
        resp = Response(js, status=405, mimetype='application/json')
        return resp

    _logger.securitylog(__name__,
                        'alliance audit',
                        ipaddress=ipaddress,
                        charid=charid,
                        detail='alliance {0}'.format(allianceid))

    # ALL is a meta-endpoint for all the vanguard alliances, plus viral

    viral = 99003916
    alliances = [allianceid]

    if allianceid == 'ALL':
        alliances = vg_alliances()
        alliances.append(viral)
    # check for auth groups

    allowed_roles = ['tsadmin', 'command']
    dn = 'ou=People,dc=triumvirate,dc=rocks'

    code, result = _ldaphelpers.ldap_search(__name__, dn,
                                            '(uid={})'.format(charid),
                                            ['authGroup'])

    if code == 'error':
        error = 'unable to check auth groups roles for {0}: ({1}) {2}'.format(
            charid, code, result)
        _logger.log('[' + __name__ + ']' + error, _logger.LogLevel.ERROR)
        js = json.dumps({'error': error})
        resp = Response(js, status=500, mimetype='application/json')
        return resp

    if result == None:
        msg = 'charid {0} not in ldap'.format(charid)
        _logger.log('[' + __name__ + '] {}'.format(msg),
                    _logger.LogLevel.ERROR)
        js = json.dumps({'error': msg})
        return Response(js, status=404, mimetype='application/json')

    (_, result), = result.items()

    if not "command" in result['authGroup']:
        error = 'insufficient corporate roles to access this endpoint.'
        _logger.log('[' + __name__ + '] ' + error, _logger.LogLevel.INFO)
        js = json.dumps({'error': error})
        resp = Response(js, status=403, mimetype='application/json')
        return resp

    alliancedata = defaultdict(list)

    # process each alliance with an individual process

    with ProcessPoolExecutor(15) as executor:
        futures = {
            executor.submit(alliance_data, charid, alliance): allianceid
            for alliance in alliances
        }
        for future in as_completed(futures):
            data = future.result()
            alliance_id = data['alliance_id']
            alliance_name = data['alliance_name']

            alliancedata[alliance_id] = dict()
            alliancedata[alliance_id]['name'] = alliance_name
            alliancedata[alliance_id]['corp_data'] = data['alliance_corps']

    js = json.dumps(alliancedata)
    resp = Response(js, status=200, mimetype='application/json')

    return resp
Beispiel #7
0
def core_fleets(char_id):
    from flask import Response, request
    from json import dumps

    import common.logger as _logger
    import common.database as _database
    import common.ldaphelpers as _ldaphelpers
    import MySQLdb as mysql
    import json

    try:
        sql_conn = mysql.connect(database=_database.DB_DATABASE,
                                 user=_database.DB_USERNAME,
                                 password=_database.DB_PASSWORD,
                                 host=_database.DB_HOST)
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql error: ' + str(err),
                    _logger.LogLevel.ERROR)
        js = json.dumps({'error': str(err)})
        resp = Response(js, status=500, mimetype='application/json')
        return resp

    cursor = sql_conn.cursor()

    query = 'SELECT idCoreOpsBoard,Time,FC,Type,Doctrine,Hype,PostedBy,Scope,authgroup FROM OpsBoard WHERE Time > NOW() ORDER BY Time ASC'
    try:
        rowcount = cursor.execute(query)
        rows = cursor.fetchall()
    except mysql.Error as err:
        _logger.log('[' + __name__ + '] mysql error: ' + str(err),
                    _logger.LogLevel.ERROR)
        js = json.dumps({'error': str(err)})
        resp = Response(js, status=500, mimetype='application/json')
        return resp
    finally:
        cursor.close()

    dn = 'ou=People,dc=triumvirate,dc=rocks'
    filterstr = 'uid={}'.format(char_id)
    attributes = [
        'corporation', 'authGroup', 'characterName', 'corporationName'
    ]
    code, result = _ldaphelpers.ldap_search(__name__, dn, filterstr,
                                            attributes)

    if code == False:
        msg = 'unable to connect to ldap'
        _logger.log('[' + __name__ + '] {0}'.format(msg),
                    _logger.LogLevel.ERROR)
        js = json.dumps({'error': msg})
        resp = Response(js, status=500, mimetype='application/json')
        return resp

    if result is not None:

        (dn, info), = result.items()

        name = info['characterName']
        groups = info['authGroup']
        corp = int(info['corporation'])
        corpName = info['corporationName']

        fleets = []

        for row in rows:

            fleet_type = row[3]

            if fleet_type == "THIRD PARTY FIGHT":
                fleet_type = "FLEET"

            fleet = {
                'id': row[0],
                'time': row[1].isoformat(),
                'fc': row[2],
                'type': fleet_type,
                'doctrine': row[4],
                'text': row[5],
                'by': row[6],
                'modifiable': False
            }

            if 'skyteam' in groups:
                fleet['modifiable'] = True
            elif name == row[6]:
                fleet['modifiable'] = True

            scope = int(row[7])

            if scope == 1 and 'vanguard' in groups:
                fleet['group'] = 'vanguard'
                fleets.append(fleet)
            elif scope == 2 and 'triumvirate' in groups:
                fleet['group'] = 'triumvirate'
                fleets.append(fleet)
            elif scope == corp:
                fleet['group'] = corpName
                fleets.append(fleet)
        return Response(json.dumps(fleets),
                        status=200,
                        mimetype='application/json')
    else:
        return Response({'error': 'not found'},
                        status=404,
                        mimetype='application/json')