613 lines
15 KiB
Ucode
613 lines
15 KiB
Ucode
//=============================================================================
|
|
// AccessControl.
|
|
//
|
|
// AccessControl is a helper class for GameInfo.
|
|
// The AccessControl class determines whether or not the player is allowed to
|
|
// login in the PreLogin() function, and also controls whether or not a player
|
|
// can enter as a spectator or a game administrator.
|
|
//
|
|
//=============================================================================
|
|
class AccessControl extends Info Config;
|
|
|
|
struct AdminPlayer
|
|
{
|
|
var xAdminUser User;
|
|
var PlayerReplicationInfo PRI;
|
|
};
|
|
|
|
var xAdminUserList Users;
|
|
var xAdminGroupList Groups;
|
|
var protected array<AdminPlayer> LoggedAdmins;
|
|
var config array< class<xPrivilegeBase> > PrivClasses;
|
|
var array<xPrivilegeBase> PrivManagers;
|
|
var string AllPrivs;
|
|
|
|
var globalconfig array<string> IPPolicies;
|
|
var localized string IPBanned;
|
|
var localized string WrongPassword;
|
|
var localized string NeedPassword;
|
|
var localized string SessionBanned;
|
|
var localized string KickedMsg;
|
|
var localized string DefaultKickReason;
|
|
var localized string IdleKickReason;
|
|
var class<AdminBase> AdminClass;
|
|
|
|
var bool bReplyToGUI;
|
|
var bool bDontAddDefaultAdmin;
|
|
|
|
var private string AdminName;
|
|
var private globalconfig string AdminPassword; // Password to receive bAdmin privileges.
|
|
var private globalconfig string GamePassword; // Password to enter game.
|
|
var globalconfig float LoginDelaySeconds; // Delay between login attempts
|
|
|
|
var globalconfig bool bBanByID; // Set to true to ban by CDKey hash
|
|
var globalconfig Array<string> BannedIDs; // Holds information about how got banned
|
|
|
|
var transient Array<string> SessionIPPolicies; // sjs
|
|
var transient array<string> SessionBannedIDs;
|
|
|
|
const PROPNUM = 4;
|
|
var localized string ACDisplayText[PROPNUM];
|
|
var localized string ACDescText[PROPNUM];
|
|
|
|
event PreBeginPlay()
|
|
{
|
|
local xAdminUser NewUser;
|
|
|
|
Super.PreBeginPlay();
|
|
|
|
assert( Users == None );
|
|
Users = new(Level.xLevel) class'xAdminUserList';
|
|
assert( Groups == None );
|
|
Groups = new(Level.xLevel) class'xAdminGroupList';
|
|
|
|
if (!bDontAddDefaultAdmin)
|
|
{
|
|
Groups.Add(Groups.CreateGroup("Admin", "", 255));
|
|
NewUser = Users.Create(AdminName, AdminPassword, "");
|
|
NewUser.AddGroup(Groups.FindByName("Admin"));
|
|
Users.Add(NewUser);
|
|
AdminName = "Admin";
|
|
}
|
|
InitPrivs();
|
|
}
|
|
|
|
function InitPrivs();
|
|
|
|
function SaveAdmins()
|
|
{
|
|
AdminPassword = Users.Get(0).Password;
|
|
}
|
|
|
|
//if _RO_
|
|
function bool AdminLoginSilent( PlayerController P, string Username, string Password)
|
|
{
|
|
if ( ValidLogin(Username, Password) )
|
|
{
|
|
P.PlayerReplicationInfo.bSilentAdmin = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
//end _RO_
|
|
|
|
function bool AdminLogin( PlayerController P, string Username, string Password)
|
|
{
|
|
if ( ValidLogin(Username, Password) )
|
|
{
|
|
P.PlayerReplicationInfo.bAdmin = true;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool AdminLogout( PlayerController P )
|
|
{
|
|
//if _RO_
|
|
if (P.PlayerReplicationInfo.bAdmin || P.PlayerReplicationInfo.bSilentAdmin)
|
|
//else
|
|
//if (P.PlayerReplicationInfo.bAdmin)
|
|
//end _RO_
|
|
{
|
|
P.PlayerReplicationInfo.bAdmin = false;
|
|
//if _RO_
|
|
P.PlayerReplicationInfo.bSilentAdmin = false;
|
|
//end _RO_
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function AdminEntered( PlayerController P, string Username)
|
|
{
|
|
Log(P.PlayerReplicationInfo.PlayerName@"logged in as Administrator.");
|
|
Level.Game.Broadcast( P, P.PlayerReplicationInfo.PlayerName@"logged in as a server administrator." );
|
|
}
|
|
|
|
function AdminExited( PlayerController P )
|
|
{
|
|
Log(P.PlayerReplicationInfo.PlayerName@"logged out.");
|
|
Level.Game.Broadcast( P, P.PlayerReplicationInfo.PlayerName@"gave up administrator abilities.");
|
|
}
|
|
|
|
function bool IsAdmin(PlayerController P)
|
|
{
|
|
return P.PlayerReplicationInfo.bAdmin;
|
|
}
|
|
|
|
function SetAdminFromURL(string N, string P)
|
|
{
|
|
local xAdminUser NewUser;
|
|
local xAdminGroup NewGroup;
|
|
|
|
Log("SetAdminFromURL called");
|
|
NewGroup = Groups.CreateGroup("URL::Admin", "", 255);
|
|
NewGroup.bMasterAdmin = true;
|
|
Groups.Add(NewGroup);
|
|
NewUser = Users.Create(N, P, "");
|
|
NewUser.AddGroup(NewGroup);
|
|
Users.Add(NewUser);
|
|
AdminName = N;
|
|
SetAdminPassword(P);
|
|
}
|
|
|
|
function bool SetAdminPassword(string P)
|
|
{
|
|
AdminPassword = P;
|
|
return true;
|
|
}
|
|
|
|
function SetGamePassword(string P)
|
|
{
|
|
GamePassword = P;
|
|
}
|
|
|
|
function bool RequiresPassword()
|
|
{
|
|
return GamePassword != "";
|
|
}
|
|
|
|
function xAdminUser GetAdmin( PlayerController PC)
|
|
{
|
|
return None;
|
|
}
|
|
|
|
function string GetAdminName( PlayerController PC)
|
|
{
|
|
return AdminName;
|
|
}
|
|
|
|
function Kick( string S )
|
|
{
|
|
local Controller C, NextC;
|
|
|
|
for ( C=Level.ControllerList; C!=None; C=NextC )
|
|
{
|
|
NextC = C.NextController;
|
|
if ( C.PlayerReplicationInfo != None && C.PlayerReplicationInfo.PlayerName~=S )
|
|
{
|
|
if (PlayerController(C) != None)
|
|
KickPlayer(PlayerController(C));
|
|
else if ( C.PlayerReplicationInfo.bBot )
|
|
{
|
|
if (C.Pawn != none && Vehicle(C.Pawn) == none)
|
|
C.Pawn.Destroy();
|
|
if (C != None)
|
|
C.Destroy();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function SessionKickBan( string S ) // sjs
|
|
{
|
|
local PlayerController P;
|
|
|
|
ForEach DynamicActors(class'PlayerController', P)
|
|
if ( P.PlayerReplicationInfo.PlayerName~=S
|
|
&& (NetConnection(P.Player)!=None) )
|
|
{
|
|
BanPlayer(P, true);
|
|
}
|
|
}
|
|
|
|
function KickBan( string S )
|
|
{
|
|
local PlayerController P;
|
|
|
|
ForEach DynamicActors(class'PlayerController', P)
|
|
if ( P.PlayerReplicationInfo.PlayerName~=S
|
|
&& (NetConnection(P.Player)!=None) )
|
|
{
|
|
BanPlayer(P);
|
|
return;
|
|
}
|
|
}
|
|
|
|
function bool KickPlayer(PlayerController C)
|
|
{
|
|
// Do not kick logged admins
|
|
if (C != None && !IsAdmin(C) && NetConnection(C.Player)!=None )
|
|
{
|
|
// TODO implement a way for admins to specify the reason
|
|
C.ClientNetworkMessage("AC_Kicked",DefaultKickReason);
|
|
if (C.Pawn != none && Vehicle(C.Pawn) == none)
|
|
C.Pawn.Destroy();
|
|
if (C != None)
|
|
C.Destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool BanPlayer(PlayerController C, optional bool bSession)
|
|
{
|
|
local string IP;
|
|
|
|
if (IsAdmin(C))
|
|
return false;
|
|
|
|
IP = C.GetPlayerNetworkAddress();
|
|
if( CheckIPPolicy(IP) == 0 )
|
|
{
|
|
IP = Left(IP, InStr(IP, ":"));
|
|
if (bSession)
|
|
{
|
|
Log("Adding Session Ban for: "$IP@C.GetPlayerIDHash()@C.PlayerReplicationInfo.PlayerName);
|
|
|
|
if (bBanByID)
|
|
SessionBannedIDs[SessionBannedIDs.Length] = C.GetPlayerIDHash()@C.PlayerReplicationInfo.PlayerName;
|
|
else
|
|
SessionIPPolicies[SessionIPPolicies.Length] = "DENY;"$IP;
|
|
|
|
SaveConfig();
|
|
C.ClientNetworkMessage("AC_SessionBan",Level.Game.GameReplicationInfo.AdminEmail);
|
|
}
|
|
else
|
|
{
|
|
Log("Adding Global Ban for: "$IP@C.GetPlayerIDHash()@C.PlayerReplicationInfo.PlayerName);
|
|
|
|
if (bBanByID)
|
|
BannedIDs[BannedIDs.Length] = C.GetPlayerIDHash()@C.PlayerReplicationInfo.PlayerName;
|
|
else
|
|
IPPolicies[IPPolicies.Length] = "DENY;"$IP;
|
|
|
|
SaveConfig();
|
|
C.ClientNetworkMessage("AC_Ban",Level.Game.GameReplicationInfo.AdminEmail);
|
|
}
|
|
|
|
if ( C.Pawn != None && Vehicle(C.Pawn) == none )
|
|
C.Pawn.Destroy();
|
|
C.Destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool KickBanPlayer(PlayerController P)
|
|
{
|
|
local string IP;
|
|
|
|
if (!IsAdmin(P))
|
|
{
|
|
IP = P.GetPlayerNetworkAddress();
|
|
if( CheckIPPolicy(IP) == 0 )
|
|
{
|
|
IP = Left(IP, InStr(IP, ":"));
|
|
Log("Adding Global Ban for: "$IP@P.GetPlayerIDHash()@P.PlayerReplicationInfo.PlayerName);
|
|
|
|
if (bBanById)
|
|
BannedIDs[BannedIDs.Length] = P.GetPlayerIDHash()@P.PlayerReplicationInfo.PlayerName;
|
|
else
|
|
IPPolicies[IPPolicies.Length] = "DENY;"$IP;
|
|
|
|
SaveConfig();
|
|
P.ClientNetworkMessage("AC_Ban",Level.Game.GameReplicationInfo.AdminEmail);
|
|
}
|
|
else P.ClientNetworkMessage("AC_Kicked",DefaultKickReason);
|
|
if ( P.Pawn != None && Vehicle(P.Pawn) == none)
|
|
P.Pawn.Destroy();
|
|
|
|
P.Destroy();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function bool CheckOptionsAdmin( string Options)
|
|
{
|
|
local string InAdminName, InPassword;
|
|
|
|
InPassword = Level.Game.ParseOption( Options, "Password" );
|
|
InAdminName= Level.Game.ParseOption( Options, "AdminName" );
|
|
return ValidLogin(InAdminName, InPassword);
|
|
}
|
|
|
|
function bool ValidLogin(string UserName, string Password)
|
|
{
|
|
return (AdminPassword != "" && Password==AdminPassword);
|
|
}
|
|
|
|
function xAdminUser GetLoggedAdmin(PlayerController P)
|
|
{
|
|
return Users.Get(0);
|
|
}
|
|
|
|
function xAdminUser GetUser(string uname)
|
|
{
|
|
return None;
|
|
}
|
|
|
|
//
|
|
// Accept or reject a player on the server.
|
|
// Fails login if you set the Error to a non-empty string.
|
|
//
|
|
event PreLogin
|
|
(
|
|
string Options,
|
|
string Address,
|
|
string PlayerID,
|
|
out string Error,
|
|
out string FailCode,
|
|
bool bSpectator
|
|
)
|
|
{
|
|
// Do any name or password or name validation here.
|
|
local string InPassword;
|
|
local bool bAdmin;
|
|
local int Result;
|
|
|
|
Error="";
|
|
InPassword = Level.Game.ParseOption( Options, "Password" );
|
|
bAdmin = CheckOptionsAdmin(Options);
|
|
|
|
if( (Level.NetMode != NM_Standalone) && !bAdmin && Level.Game.AtCapacity(bSpectator) )
|
|
{
|
|
// TODO: Check Login to make room for Master Admins if not enuff specs.
|
|
|
|
FailCode="SERVERFULL";
|
|
// Error=Level.Game.GameMessageClass.Default.MaxedOutMessage;
|
|
|
|
// Must clear error string so that client doesn't receive additional connection failed messages
|
|
Error = "";
|
|
}
|
|
else if ( GamePassword!="" && caps(InPassword)!=caps(GamePassword) && !bAdmin )
|
|
{
|
|
if( InPassword == "" )
|
|
{
|
|
Error = "";
|
|
FailCode = "NEEDPW";
|
|
}
|
|
else
|
|
{
|
|
Error = "";
|
|
FailCode = "WRONGPW";
|
|
}
|
|
}
|
|
|
|
Result = CheckIPPolicy(Address);
|
|
if ( Result == 0 && bBanByID )
|
|
Result = CheckID(PlayerID);
|
|
|
|
if ( Result > 0 )
|
|
{
|
|
if ( Result == 1 )
|
|
{
|
|
Error = "";
|
|
FailCode = "SESSIONBAN";
|
|
}
|
|
|
|
else if ( Result == 2 )
|
|
{
|
|
Error = "";
|
|
FailCode = "LOCALBAN";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 0 - accept, 1 - sessionban, 2 - permanent ban
|
|
function int CheckIPPolicy(string Address, optional bool bSilent)
|
|
{
|
|
local int i, j, LastMatchingPolicy;
|
|
local string Policy, Mask;
|
|
local bool bAcceptAddress, bAcceptPolicy;
|
|
|
|
// strip port number
|
|
j = InStr(Address, ":");
|
|
if(j != -1)
|
|
Address = Left(Address, j);
|
|
|
|
bAcceptAddress = True;
|
|
for(i=0; i<IPPolicies.Length; i++)
|
|
{
|
|
if ( Divide( IPPolicies[i], ";", Policy, Mask ) )
|
|
{
|
|
if(Policy ~= "ACCEPT")
|
|
bAcceptPolicy = True;
|
|
else if(Policy ~= "DENY")
|
|
bAcceptPolicy = False;
|
|
else
|
|
continue;
|
|
|
|
j = InStr(Mask, "*");
|
|
if(j != -1)
|
|
{
|
|
if(Left(Mask, j) == Left(Address, j))
|
|
{
|
|
bAcceptAddress = bAcceptPolicy;
|
|
LastMatchingPolicy = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(Mask == Address)
|
|
{
|
|
bAcceptAddress = bAcceptPolicy;
|
|
LastMatchingPolicy = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bAcceptAddress )
|
|
{
|
|
if ( !bSilent )
|
|
Log("Denied connection for "$Address$" with IP policy "$IPPolicies[LastMatchingPolicy]);
|
|
|
|
return 2;
|
|
}
|
|
|
|
// check session polices
|
|
for(i=0; i<SessionIPPolicies.Length && SessionIPPolicies[i] != ""; i++ )
|
|
{
|
|
Divide( SessionIPPolicies[i], ";", Policy, Mask );
|
|
if(Policy ~= "ACCEPT")
|
|
bAcceptPolicy = True;
|
|
else if(Policy ~= "DENY")
|
|
bAcceptPolicy = False;
|
|
else
|
|
continue;
|
|
|
|
j = InStr(Mask, "*");
|
|
if(j != -1)
|
|
{
|
|
if(Left(Mask, j) == Left(Address, j))
|
|
{
|
|
bAcceptAddress = bAcceptPolicy;
|
|
LastMatchingPolicy = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(Mask == Address)
|
|
{
|
|
bAcceptAddress = bAcceptPolicy;
|
|
LastMatchingPolicy = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !bAcceptAddress )
|
|
{
|
|
if ( !bSilent )
|
|
Log("Denied connection for "$Address$" with Session IP policy "$SessionIPPolicies[LastMatchingPolicy]);
|
|
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Stubs in preparation of multi-admin system
|
|
function bool CanPerform(PlayerController P, string Action)
|
|
{
|
|
// Filter out any Admin Users/Group/commands
|
|
if (!AllowPriv(Action))
|
|
return false;
|
|
|
|
// Standard Admin actions only performed by Admin
|
|
//if _RO_
|
|
return P.PlayerReplicationInfo.bAdmin || P.PlayerReplicationInfo.bSilentAdmin;
|
|
//else
|
|
//return P.PlayerReplicationInfo.bAdmin;
|
|
//end _RO_
|
|
}
|
|
|
|
function bool AllowPriv(string priv)
|
|
{
|
|
if (Left(priv, 1) ~= "A" || Left(priv, 1) ~= "G")
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static function FillPlayInfo(PlayInfo PlayInfo)
|
|
{
|
|
local int i;
|
|
|
|
Super.FillPlayInfo(PlayInfo); // Always begin with calling parent
|
|
|
|
i=0;
|
|
PlayInfo.AddSetting(default.ServerGroup, "GamePassword", default.ACDisplayText[i++], 240, 1, "Text", "16",,True,True);
|
|
PlayInfo.AddSetting(default.ServerGroup, "IPPolicies", default.ACDisplayText[i++], 254, 1, "Text", "15",,True,True);
|
|
PlayInfo.AddSetting(default.ServerGroup, "AdminPassword", default.ACDisplayText[i++], 255, 1, "Text", "16",,True,True);
|
|
PlayInfo.AddSetting(default.ServerGroup, "LoginDelaySeconds", default.ACDisplayText[i++], 200, 1, "Text", "3;0:999",,True,True);
|
|
}
|
|
|
|
static event string GetDescriptionText(string PropName)
|
|
{
|
|
switch (PropName)
|
|
{
|
|
case "GamePassword": return default.ACDescText[0];
|
|
case "IPPolicies": return default.ACDescText[1];
|
|
case "AdminPassword": return default.ACDescText[2];
|
|
case "LoginDelaySeconds": return default.ACDescText[3];
|
|
}
|
|
|
|
return Super.GetDescriptionText(PropName);
|
|
}
|
|
|
|
// 0 - ok, 1 - session, 2 - perm
|
|
// STEAM AUTH Minor changes to handle steamids justin h
|
|
function int CheckID(string CDHash)
|
|
{
|
|
local int i;
|
|
local string id;
|
|
|
|
//log("AccessControl::CheckID "$CDHash);
|
|
|
|
// IF _RO_
|
|
// if ( class'LevelInfo'.static.IsDemoBuild() )
|
|
// return 0;
|
|
|
|
|
|
for (i=0;i<BannedIDs.Length;i++)
|
|
{
|
|
id = Left(BannedIDs[i], InStr(BannedIDs[i], " "));
|
|
|
|
// Use the old system if the Steam system isn't enabled
|
|
if( id == "" )
|
|
id = Left(BannedIDs[i],32);
|
|
|
|
if ( CDHash ~= id)//STEAMAUTH -- ~=Left(BannedIDs[i],32) )
|
|
return 2;
|
|
}
|
|
|
|
for (i=0;i<SessionBannedIDs.Length;i++)
|
|
{
|
|
id = Left(SessionBannedIDs[i], InStr(SessionBannedIDs[i], " "));
|
|
|
|
// Use the old system if the Steam system isn't enabled
|
|
if( id == "" )
|
|
id = Left(SessionBannedIDs[i],32);
|
|
|
|
if ( CDHash ~= id)//STEAMAUTH -- ~=Left(SessionBannedIDs[i],32) )
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
DefaultKickReason="None specified"
|
|
IdleKickReason="Kicked for idling."
|
|
WrongPassword="The password you entered is incorrect."
|
|
NeedPassword="You need to enter a password to join this game."
|
|
IPBanned="Your IP address has been banned on this server."
|
|
SessionBanned="Your IP address has been banned from the current game session."
|
|
IPPolicies(0)="ACCEPT;*"
|
|
AdminClass=class'Engine.Admin'
|
|
KickedMsg="You have been forcibly removed from the game."
|
|
ACDisplayText(0)="Game Password"
|
|
ACDisplayText(1)="Access Policies"
|
|
ACDisplayText(2)="Admin Password"
|
|
ACDisplayText(3)="Login Delay"
|
|
ACDescText(0)="If this password is set, players will have to enter it to join this server."
|
|
ACDescText(1)="Specifies IP addresses or address ranges which have been banned."
|
|
ACDescText(2)="Password required to login with administrator privileges on this server."
|
|
ACDescText(3)="Number of seconds user must wait after an unsuccessful login attempt before able to login again."
|
|
}
|