2022-04-20 11:45:30 -04:00
let vpnExceptionIds = [ ] ;
2022-10-17 10:17:43 -04:00
const vpnAllowListKey = 'Webfront::Nav::Admin::VPNAllowList' ;
const vpnWhitelistKey = 'Webfront::Profile::VPNWhitelist' ;
2022-03-23 14:34:04 -04:00
const commands = [ {
2022-10-12 22:06:18 -04:00
name : 'whitelistvpn' ,
description : 'whitelists a player\'s client id from VPN detection' ,
alias : 'wv' ,
permission : 'SeniorAdmin' ,
2022-03-23 14:34:04 -04:00
targetRequired : true ,
arguments : [ {
2022-10-12 22:06:18 -04:00
name : 'player' ,
2022-03-23 14:34:04 -04:00
required : true
} ] ,
execute : ( gameEvent ) => {
2022-04-20 11:45:30 -04:00
vpnExceptionIds . push ( gameEvent . Target . ClientId ) ;
plugin . configHandler . SetValue ( 'vpnExceptionIds' , vpnExceptionIds ) ;
2022-03-23 14:34:04 -04:00
gameEvent . Origin . Tell ( ` Successfully whitelisted ${ gameEvent . Target . Name } ` ) ;
}
2022-10-17 10:17:43 -04:00
} ,
{
name : 'disallowvpn' ,
description : 'disallows a player from connecting with a VPN' ,
alias : 'dv' ,
permission : 'SeniorAdmin' ,
targetRequired : true ,
arguments : [ {
name : 'player' ,
required : true
} ] ,
execute : ( gameEvent ) => {
vpnExceptionIds = vpnExceptionIds . filter ( exception => parseInt ( exception ) !== parseInt ( gameEvent . Target . ClientId ) ) ;
plugin . configHandler . SetValue ( 'vpnExceptionIds' , vpnExceptionIds ) ;
gameEvent . Origin . Tell ( ` Successfully disallowed ${ gameEvent . Target . Name } from connecting with VPN ` ) ;
}
2022-03-23 14:34:04 -04:00
} ] ;
2022-10-17 10:17:43 -04:00
const getClientsData = ( clientIds ) => {
const contextFactory = _serviceResolver . ResolveService ( 'IDatabaseContextFactory' ) ;
const context = contextFactory . CreateContext ( false ) ;
const clientSet = context . Clients ;
const clients = clientSet . GetClientsBasicData ( clientIds ) ;
context . Dispose ( ) ;
return clients ;
}
2022-03-23 14:34:04 -04:00
const plugin = {
2018-08-26 20:20:47 -04:00
author : 'RaidMax' ,
2022-10-12 22:06:18 -04:00
version : 1.5 ,
2018-09-04 13:40:29 -04:00
name : 'VPN Detection Plugin' ,
2018-08-26 20:20:47 -04:00
manager : null ,
logger : null ,
2022-04-20 11:45:30 -04:00
2018-09-04 13:40:29 -04:00
checkForVpn : function ( origin ) {
2022-03-23 14:34:04 -04:00
let exempt = false ;
2018-08-26 20:20:47 -04:00
// prevent players that are exempt from being kicked
2022-04-20 11:45:30 -04:00
vpnExceptionIds . forEach ( function ( id ) {
2022-10-17 10:17:43 -04:00
if ( parseInt ( id ) === parseInt ( origin . ClientId ) ) {
2018-08-26 20:20:47 -04:00
exempt = true ;
return false ;
}
} ) ;
if ( exempt ) {
2022-03-23 14:34:04 -04:00
this . logger . WriteInfo ( ` ${ origin } is whitelisted, so we are not checking VPN status ` ) ;
2018-08-26 20:20:47 -04:00
return ;
}
2022-03-23 14:34:04 -04:00
let usingVPN = false ;
2018-08-26 20:20:47 -04:00
try {
2022-03-23 14:34:04 -04:00
const cl = new System . Net . Http . HttpClient ( ) ;
const re = cl . GetAsync ( ` https://api.xdefcon.com/proxy/check/?ip= ${ origin . IPAddressString } ` ) . Result ;
const userAgent = ` IW4MAdmin- ${ this . manager . GetApplicationSettings ( ) . Configuration ( ) . Id } ` ;
2021-04-07 09:47:42 -04:00
cl . DefaultRequestHeaders . Add ( 'User-Agent' , userAgent ) ;
2022-03-23 14:34:04 -04:00
const co = re . Content ;
const parsedJSON = JSON . parse ( co . ReadAsStringAsync ( ) . Result ) ;
2018-09-04 13:40:29 -04:00
co . Dispose ( ) ;
re . Dispose ( ) ;
cl . Dispose ( ) ;
2021-04-07 10:53:32 -04:00
usingVPN = parsedJSON . success && parsedJSON . proxy ;
2018-08-26 20:20:47 -04:00
} catch ( e ) {
2022-03-23 14:34:04 -04:00
this . logger . WriteWarning ( ` There was a problem checking client IP for VPN ${ e . message } ` ) ;
2018-08-26 20:20:47 -04:00
}
if ( usingVPN ) {
2018-09-02 17:59:27 -04:00
this . logger . WriteInfo ( origin + ' is using a VPN (' + origin . IPAddressString + ')' ) ;
2022-03-23 14:34:04 -04:00
const contactUrl = this . manager . GetApplicationSettings ( ) . Configuration ( ) . ContactUri ;
let additionalInfo = '' ;
2021-04-07 09:47:42 -04:00
if ( contactUrl ) {
2022-03-23 14:34:04 -04:00
additionalInfo = _localization . LocalizationIndex [ 'SERVER_KICK_VPNS_NOTALLOWED_INFO' ] + ' ' + contactUrl ;
2021-04-07 09:47:42 -04:00
}
2022-03-23 14:34:04 -04:00
origin . Kick ( _localization . LocalizationIndex [ 'SERVER_KICK_VPNS_NOTALLOWED' ] + ' ' + additionalInfo , _IW4MAdminClient ) ;
2018-08-26 20:20:47 -04:00
}
} ,
2018-09-04 13:40:29 -04:00
onEventAsync : function ( gameEvent , server ) {
2019-05-29 17:55:35 -04:00
// join event
2022-03-23 14:34:04 -04:00
if ( gameEvent . TypeName === 'Join' ) {
2018-08-30 21:53:00 -04:00
this . checkForVpn ( gameEvent . Origin ) ;
2018-08-26 20:20:47 -04:00
}
} ,
2018-09-04 13:40:29 -04:00
onLoadAsync : function ( manager ) {
2018-08-26 20:20:47 -04:00
this . manager = manager ;
2018-10-06 16:31:05 -04:00
this . logger = manager . GetLogger ( 0 ) ;
2022-03-23 14:34:04 -04:00
this . configHandler = _configHandler ;
2022-10-12 22:06:18 -04:00
this . configHandler . GetValue ( 'vpnExceptionIds' ) . forEach ( element => vpnExceptionIds . push ( parseInt ( element ) ) ) ;
2022-04-20 11:45:30 -04:00
this . logger . WriteInfo ( ` Loaded ${ vpnExceptionIds . length } ids into whitelist ` ) ;
2022-09-08 16:03:38 -04:00
this . interactionRegistration = _serviceResolver . ResolveService ( 'IInteractionRegistration' ) ;
2022-10-17 10:17:43 -04:00
// registers the profile action
this . interactionRegistration . RegisterScriptInteraction ( vpnWhitelistKey , this . name , ( targetId , game , token ) => {
2022-09-08 16:03:38 -04:00
const helpers = importNamespace ( 'SharedLibraryCore.Helpers' ) ;
const interactionData = new helpers . InteractionData ( ) ;
2022-10-17 10:17:43 -04:00
interactionData . ActionPath = 'DynamicAction' ;
interactionData . InteractionId = vpnWhitelistKey ;
2022-10-12 22:06:18 -04:00
interactionData . EntityId = targetId ;
2022-10-17 10:17:43 -04:00
interactionData . MinimumPermission = 3 ;
interactionData . Source = this . name ;
interactionData . ActionMeta . Add ( 'InteractionId' , 'command' ) ; // indicate we're wanting to execute a command
interactionData . ActionMeta . Add ( 'ShouldRefresh' , true . toString ( ) ) ; // indicates that the page should refresh after performing the action
2022-09-08 16:03:38 -04:00
2022-10-17 10:17:43 -04:00
if ( vpnExceptionIds . includes ( targetId ) ) {
interactionData . Name = _localization . LocalizationIndex [ 'WEBFRONT_VPN_BUTTON_DISALLOW' ] ; // text for the profile button
interactionData . DisplayMeta = 'oi-circle-x' ;
interactionData . ActionMeta . Add ( 'Data' , ` disallowvpn ` ) ; // command to execute
interactionData . ActionMeta . Add ( 'ActionButtonLabel' , _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_DISALLOW_CONFIRM' ] ) ; // confirm button on the dialog
interactionData . ActionMeta . Add ( 'Name' , _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_DISALLOW_TITLE' ] ) ; // title on the confirm dialog
} else {
interactionData . Name = _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_ALLOW' ] ; // text for the profile button
interactionData . DisplayMeta = 'oi-circle-check' ;
interactionData . ActionMeta . Add ( 'Data' , ` whitelistvpn ` ) ; // command to execute
interactionData . ActionMeta . Add ( 'ActionButtonLabel' , _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_ALLOW_CONFIRM' ] ) ; // confirm button on the dialog
interactionData . ActionMeta . Add ( 'Name' , _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_ALLOW_TITLE' ] ) ; // title on the confirm dialog
}
2022-09-08 16:03:38 -04:00
2022-10-17 10:17:43 -04:00
return interactionData ;
} ) ;
// registers the navigation/page
this . interactionRegistration . RegisterScriptInteraction ( vpnAllowListKey , this . name , ( targetId , game , token ) => {
const helpers = importNamespace ( 'SharedLibraryCore.Helpers' ) ;
const interactionData = new helpers . InteractionData ( ) ;
interactionData . Name = _localization . LocalizationIndex [ 'WEBFRONT_NAV_VPN_TITLE' ] ; // navigation link name
interactionData . Description = _localization . LocalizationIndex [ 'WEBFRONT_NAV_VPN_DESC' ] ; // alt and title
interactionData . DisplayMeta = 'oi-circle-check' ; // nav icon
interactionData . InteractionId = vpnAllowListKey ;
interactionData . MinimumPermission = 3 ; // moderator
interactionData . InteractionType = 2 ; // 1 is RawContent for apis etc..., 2 is
2022-09-08 16:03:38 -04:00
interactionData . Source = this . name ;
2022-10-17 10:17:43 -04:00
interactionData . ScriptAction = ( sourceId , targetId , game , meta , token ) => {
const clientsData = getClientsData ( vpnExceptionIds ) ;
let table = '<table class="table bg-dark-dm bg-light-lm">' ;
const disallowInteraction = {
InteractionId : 'command' ,
Data : 'disallowvpn' ,
ActionButtonLabel : _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_DISALLOW_CONFIRM' ] ,
Name : _localization . LocalizationIndex [ 'WEBFRONT_VPN_ACTION_DISALLOW_TITLE' ]
} ;
if ( clientsData . length === 0 )
{
table += ` <tr><td>No players are whitelisted.</td></tr> `
}
clientsData . forEach ( client => {
table += ` <tr>
< td >
< a href = "/Client/Profile/${client.ClientId}" class = "level-color-${client.Level.toLowerCase()} no-decoration" > $ { client . CurrentAlias . Name . StripColors ( ) } < / a >
< / t d >
< td >
< a href = "#" class = "profile-action no-decoration float-right" data - action = "DynamicAction" data - action - id = "${client.ClientId}"
data - action - meta = "${encodeURI(JSON.stringify(disallowInteraction))}" >
< div class = "btn" >
< i class = "oi oi-circle-x mr-5 font-size-12" > < / i >
< span class = "text-truncate" > $ { _localization . LocalizationIndex [ 'WEBFRONT_VPN_BUTTON_DISALLOW' ] } < / s p a n >
< / d i v >
< / a >
< / t d >
< / t r > ` ;
} ) ;
table += '</table>' ;
return table ;
}
2022-09-08 16:03:38 -04:00
return interactionData ;
} ) ;
2018-08-26 20:20:47 -04:00
} ,
2018-09-04 13:40:29 -04:00
onUnloadAsync : function ( ) {
2022-10-17 10:17:43 -04:00
this . interactionRegistration . UnregisterInteraction ( vpnWhitelistKey ) ;
this . interactionRegistration . UnregisterInteraction ( vpnAllowListKey ) ;
2018-09-04 13:40:29 -04:00
} ,
2018-08-26 20:20:47 -04:00
2018-09-04 13:40:29 -04:00
onTickAsync : function ( server ) {
}
2022-03-23 14:34:04 -04:00
} ;