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)
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')
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
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()
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
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
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')