2018-11-25 21:00:36 -05:00
using Microsoft.EntityFrameworkCore ;
2018-04-08 02:44:42 -04:00
using SharedLibraryCore.Database ;
using SharedLibraryCore.Database.Models ;
2019-04-25 22:05:35 -04:00
using SharedLibraryCore.Dtos ;
2020-05-24 22:22:26 -04:00
using SharedLibraryCore.Helpers ;
using SharedLibraryCore.Interfaces ;
2018-11-25 21:00:36 -05:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Threading.Tasks ;
2018-11-05 22:01:29 -05:00
using static SharedLibraryCore . Database . Models . EFClient ;
2017-11-25 20:29:58 -05:00
2018-04-08 02:44:42 -04:00
namespace SharedLibraryCore.Services
2017-11-25 20:29:58 -05:00
{
2020-05-24 22:22:26 -04:00
public class ClientService : IEntityService < EFClient > , IResourceQueryHelper < FindClientRequest , FindClientResult >
2017-11-25 20:29:58 -05:00
{
2020-05-24 22:22:26 -04:00
private readonly IDatabaseContextFactory _contextFactory ;
public ClientService ( IDatabaseContextFactory databaseContextFactory )
{
_contextFactory = databaseContextFactory ;
}
2017-11-25 20:29:58 -05:00
public async Task < EFClient > Create ( EFClient entity )
{
2017-11-29 19:35:50 -05:00
using ( var context = new DatabaseContext ( ) )
2017-11-25 20:29:58 -05:00
{
2019-04-05 14:34:03 -04:00
int? linkId = null ;
int? aliasId = null ;
2020-04-04 13:40:23 -04:00
entity . Name = entity . Name . CapClientName ( EFAlias . MAX_NAME_LENGTH ) ;
2019-04-05 14:34:03 -04:00
if ( entity . IPAddress ! = null )
{
2019-11-15 15:50:20 -05:00
var existingAliases = await context . Aliases
2019-04-05 14:34:03 -04:00
. Select ( _alias = > new { _alias . AliasId , _alias . LinkId , _alias . IPAddress , _alias . Name } )
2019-11-15 15:50:20 -05:00
. Where ( _alias = > _alias . IPAddress = = entity . IPAddress )
. ToListAsync ( ) ;
2019-04-05 14:34:03 -04:00
2019-11-15 15:50:20 -05:00
if ( existingAliases . Count > 0 )
2019-04-05 14:34:03 -04:00
{
2020-01-21 19:08:18 -05:00
linkId = existingAliases . OrderBy ( _alias = > _alias . LinkId ) . First ( ) . LinkId ;
2019-04-06 22:48:49 -04:00
2019-11-15 15:50:20 -05:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] client with new GUID {entity} has existing link {linkId}" ) ;
var existingExactAlias = existingAliases . FirstOrDefault ( _alias = > _alias . Name = = entity . Name ) ;
if ( existingExactAlias ! = null )
2019-04-05 14:34:03 -04:00
{
2019-11-15 15:50:20 -05:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] client with new GUID {entity} has existing alias {existingExactAlias.AliasId}" ) ;
aliasId = existingExactAlias . AliasId ;
2019-04-05 14:34:03 -04:00
}
}
}
2018-11-25 21:00:36 -05:00
var client = new EFClient ( )
{
Level = Permission . User ,
FirstConnection = DateTime . UtcNow ,
LastConnection = DateTime . UtcNow ,
2019-04-05 14:34:03 -04:00
NetworkId = entity . NetworkId
2018-11-25 21:00:36 -05:00
} ;
2019-12-25 22:05:57 -05:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] adding {entity} to context" ) ;
2019-04-06 22:48:49 -04:00
context . Clients . Add ( client ) ;
2019-06-11 09:00:14 -04:00
2019-04-05 22:15:17 -04:00
// they're just using a new GUID
if ( aliasId . HasValue )
2018-11-25 21:00:36 -05:00
{
2019-04-06 22:48:49 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] setting {entity}'s alias id and linkid to ({aliasId.Value}, {linkId.Value})" ) ;
2019-04-05 22:15:17 -04:00
client . CurrentAliasId = aliasId . Value ;
2019-04-05 14:34:03 -04:00
client . AliasLinkId = linkId . Value ;
}
2019-04-05 22:15:17 -04:00
// link was found but they don't have an exact alias
else if ( ! aliasId . HasValue & & linkId . HasValue )
2019-04-05 14:34:03 -04:00
{
2019-04-06 22:48:49 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] setting {entity}'s linkid to {linkId.Value}, but creating new alias" ) ;
2019-04-05 22:15:17 -04:00
client . AliasLinkId = linkId . Value ;
2019-04-06 22:48:49 -04:00
client . CurrentAlias = new EFAlias ( )
2019-04-05 22:15:17 -04:00
{
Name = entity . Name ,
2019-08-02 19:04:34 -04:00
SearchableName = entity . Name . StripColors ( ) . ToLower ( ) ,
2019-04-05 22:15:17 -04:00
DateAdded = DateTime . UtcNow ,
IPAddress = entity . IPAddress ,
LinkId = linkId . Value
} ;
2019-04-05 14:34:03 -04:00
}
2019-04-05 22:15:17 -04:00
// brand new players (supposedly)
2019-04-05 14:34:03 -04:00
else
{
2019-04-06 22:48:49 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[create] creating new Link and Alias for {entity}" ) ;
var link = new EFAliasLink ( ) ;
var alias = new EFAlias ( )
2019-04-05 14:34:03 -04:00
{
Name = entity . Name ,
2019-08-02 19:04:34 -04:00
SearchableName = entity . Name . StripColors ( ) . ToLower ( ) ,
2019-04-05 14:34:03 -04:00
DateAdded = DateTime . UtcNow ,
IPAddress = entity . IPAddress ,
2019-04-06 22:48:49 -04:00
Link = link
2019-04-05 14:34:03 -04:00
} ;
2019-04-06 22:48:49 -04:00
link . Children . Add ( alias ) ;
client . AliasLink = link ;
client . CurrentAlias = alias ;
2019-04-05 14:34:03 -04:00
}
2018-11-25 21:00:36 -05:00
await context . SaveChangesAsync ( ) ;
return client ;
}
}
2020-04-04 13:40:23 -04:00
private async Task UpdateAlias ( string originalName , int? ip , EFClient entity , DatabaseContext context )
2018-11-25 21:00:36 -05:00
{
2020-05-24 22:22:26 -04:00
string name = originalName . CapClientName ( EFAlias . MAX_NAME_LENGTH ) ;
2020-04-04 13:40:23 -04:00
2019-01-02 19:32:39 -05:00
// entity is the tracked db context item
// get all aliases by IP address and LinkId
var iqAliases = context . Aliases
. Include ( a = > a . Link )
2019-04-02 21:20:37 -04:00
// we only want alias that have the same IP address or share a link
2019-04-05 14:34:03 -04:00
. Where ( _alias = > _alias . IPAddress = = ip | | ( _alias . LinkId = = entity . AliasLinkId ) ) ;
2020-04-04 13:40:23 -04:00
2019-01-02 19:32:39 -05:00
var aliases = await iqAliases . ToListAsync ( ) ;
2020-02-06 19:35:30 -05:00
var currentIPs = aliases . Where ( _a2 = > _a2 . IPAddress ! = null ) . Select ( _a2 = > _a2 . IPAddress ) . Distinct ( ) ;
var floatingIPAliases = await context . Aliases . Where ( _alias = > currentIPs . Contains ( _alias . IPAddress ) ) . ToListAsync ( ) ;
2020-04-04 13:40:23 -04:00
aliases . AddRange ( floatingIPAliases ) ;
2019-07-16 16:27:19 -04:00
2019-01-02 19:32:39 -05:00
// see if they have a matching IP + Name but new NetworkId
2020-02-06 19:35:30 -05:00
var existingExactAlias = aliases . OrderBy ( _alias = > _alias . LinkId ) . FirstOrDefault ( a = > a . Name = = name & & a . IPAddress = = ip ) ;
2019-04-02 21:20:37 -04:00
bool hasExactAliasMatch = existingExactAlias ! = null ;
2018-11-25 21:00:36 -05:00
2019-01-02 19:32:39 -05:00
// if existing alias matches link them
2019-04-02 21:20:37 -04:00
var newAliasLink = existingExactAlias ? . Link ;
// if no exact matches find the first IP or LinkId that matches
2019-06-24 12:01:34 -04:00
newAliasLink = newAliasLink ? ? aliases . OrderBy ( _alias = > _alias . LinkId ) . FirstOrDefault ( ) ? . Link ;
2019-04-02 21:20:37 -04:00
// if no matches are found, use our current one ( it will become permanent )
newAliasLink = newAliasLink ? ? entity . AliasLink ;
2019-01-02 19:32:39 -05:00
bool hasExistingAlias = aliases . Count > 0 ;
2019-04-02 21:20:37 -04:00
bool isAliasLinkUpdated = newAliasLink . AliasLinkId ! = entity . AliasLink . AliasLinkId ;
2019-01-02 19:32:39 -05:00
2019-06-24 12:01:34 -04:00
await context . SaveChangesAsync ( ) ;
2020-02-06 19:35:30 -05:00
int distinctLinkCount = aliases . Select ( _alias = > _alias . LinkId ) . Distinct ( ) . Count ( ) ;
2019-04-02 21:20:37 -04:00
// this happens when the link we found is different than the one we create before adding an IP
2020-02-06 19:35:30 -05:00
if ( isAliasLinkUpdated | | distinctLinkCount > 1 )
2019-01-02 19:32:39 -05:00
{
2019-04-07 21:14:59 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[updatealias] found a link for {entity} so we are updating link from {entity.AliasLink.AliasLinkId} to {newAliasLink.AliasLinkId}" ) ;
2019-01-02 19:32:39 -05:00
2020-02-06 19:35:30 -05:00
var completeAliasLinkIds = aliases . Select ( _item = > _item . LinkId )
. Append ( entity . AliasLinkId )
. Distinct ( )
. ToList ( ) ;
entity . CurrentServer . Logger . WriteDebug ( $"[updatealias] updating aliasLinks {string.Join(',', completeAliasLinkIds)} for IP {ip} to {newAliasLink.AliasLinkId}" ) ;
2019-01-02 19:32:39 -05:00
2019-04-05 22:15:17 -04:00
// update all the clients that have the old alias link
await context . Clients
2020-02-06 19:35:30 -05:00
. Where ( _client = > completeAliasLinkIds . Contains ( _client . AliasLinkId ) )
2019-04-05 22:15:17 -04:00
. ForEachAsync ( _client = > _client . AliasLinkId = newAliasLink . AliasLinkId ) ;
2019-09-26 17:08:49 -04:00
// we also need to update all the penalties or they get deleted
// scenario
// link1 joins with ip1
// link2 joins with ip2,
// link2 receives penalty
// link2 joins with ip1
// pre existing link for link2 detected
// link2 is deleted
// link2 penalties are orphaned
await context . Penalties
2020-02-06 19:35:30 -05:00
. Where ( _penalty = > completeAliasLinkIds . Contains ( _penalty . LinkId ) )
2019-09-26 17:08:49 -04:00
. ForEachAsync ( _penalty = > _penalty . LinkId = newAliasLink . AliasLinkId ) ;
2019-04-02 21:20:37 -04:00
entity . AliasLink = newAliasLink ;
entity . AliasLinkId = newAliasLink . AliasLinkId ;
2018-12-31 21:52:19 -05:00
2019-04-02 21:20:37 -04:00
// update all previous aliases
await context . Aliases
2020-02-06 19:35:30 -05:00
. Where ( _alias = > completeAliasLinkIds . Contains ( _alias . LinkId ) )
2019-04-05 14:34:03 -04:00
. ForEachAsync ( _alias = > _alias . LinkId = newAliasLink . AliasLinkId ) ;
2018-12-31 21:52:19 -05:00
2019-04-02 21:20:37 -04:00
await context . SaveChangesAsync ( ) ;
// we want to delete the now inactive alias
2020-02-06 19:35:30 -05:00
if ( newAliasLink . AliasLinkId ! = entity . AliasLinkId )
{
context . AliasLinks . Remove ( entity . AliasLink ) ;
await context . SaveChangesAsync ( ) ;
}
2019-01-02 19:32:39 -05:00
}
2018-12-31 21:52:19 -05:00
2019-01-02 19:32:39 -05:00
// the existing alias matches ip and name, so we can just ignore the temporary one
2019-04-02 21:20:37 -04:00
if ( hasExactAliasMatch )
2019-01-02 19:32:39 -05:00
{
2019-04-07 21:14:59 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[updatealias] {entity} has exact alias match" ) ;
2019-04-02 21:20:37 -04:00
var oldAlias = entity . CurrentAlias ;
2019-01-02 19:32:39 -05:00
entity . CurrentAliasId = existingExactAlias . AliasId ;
entity . CurrentAlias = existingExactAlias ;
await context . SaveChangesAsync ( ) ;
2019-04-02 21:20:37 -04:00
// the alias is the same so we can just remove it
2019-04-07 21:14:59 -04:00
if ( oldAlias . AliasId ! = existingExactAlias . AliasId & & oldAlias . AliasId > 0 )
2019-04-02 21:20:37 -04:00
{
2019-04-12 23:25:18 -04:00
await context . Clients
. Where ( _client = > _client . CurrentAliasId = = oldAlias . AliasId )
. ForEachAsync ( _client = > _client . CurrentAliasId = existingExactAlias . AliasId ) ;
await context . SaveChangesAsync ( ) ;
2019-11-15 15:50:20 -05:00
if ( context . Entry ( oldAlias ) . State ! = EntityState . Deleted )
{
entity . CurrentServer . Logger . WriteDebug ( $"[updatealias] {entity} has exact alias match, so we're going to try to remove aliasId {oldAlias.AliasId} with linkId {oldAlias.AliasId}" ) ;
context . Aliases . Remove ( oldAlias ) ;
await context . SaveChangesAsync ( ) ;
}
2019-04-02 21:20:37 -04:00
}
2019-01-02 19:32:39 -05:00
}
2018-11-25 21:00:36 -05:00
2019-01-02 19:32:39 -05:00
// theres no exact match, but they've played before with the GUID or IP
2019-04-05 14:34:03 -04:00
else
2019-01-02 19:32:39 -05:00
{
2019-04-07 21:14:59 -04:00
entity . CurrentServer . Logger . WriteDebug ( $"[updatealias] {entity} is using a new alias" ) ;
2018-12-31 21:52:19 -05:00
2019-04-05 14:34:03 -04:00
var newAlias = new EFAlias ( )
2019-01-02 19:32:39 -05:00
{
2019-04-05 14:34:03 -04:00
DateAdded = DateTime . UtcNow ,
IPAddress = ip ,
LinkId = newAliasLink . AliasLinkId ,
Name = name ,
2019-08-02 19:04:34 -04:00
SearchableName = name . StripColors ( ) . ToLower ( ) ,
2019-04-05 14:34:03 -04:00
Active = true ,
} ;
2018-12-31 21:52:19 -05:00
2019-04-05 14:34:03 -04:00
entity . CurrentAlias = newAlias ;
entity . CurrentAliasId = 0 ;
2019-01-02 19:32:39 -05:00
await context . SaveChangesAsync ( ) ;
}
2019-04-02 21:20:37 -04:00
}
2018-11-25 21:00:36 -05:00
2019-04-02 21:20:37 -04:00
/// <summary>
/// updates the permission level of the given target to the given permission level
/// </summary>
/// <param name="newPermission"></param>
/// <param name="temporalClient"></param>
/// <param name="origin"></param>
/// <param name="ctx"></param>
/// <returns></returns>
2020-05-16 12:54:01 -04:00
public virtual async Task UpdateLevel ( Permission newPermission , EFClient temporalClient , EFClient origin )
2019-04-02 21:20:37 -04:00
{
using ( var ctx = new DatabaseContext ( ) )
2019-01-02 19:32:39 -05:00
{
2019-04-02 21:20:37 -04:00
var entity = await ctx . Clients
2019-05-02 23:33:38 -04:00
. Where ( _client = > _client . ClientId = = temporalClient . ClientId )
2019-04-02 21:20:37 -04:00
. FirstAsync ( ) ;
2019-04-05 14:34:03 -04:00
var oldPermission = entity . Level ;
entity . Level = newPermission ;
await ctx . SaveChangesAsync ( ) ;
#if DEBUG = = true
temporalClient . CurrentServer . Logger . WriteDebug ( $"Updated {temporalClient.ClientId} to {newPermission}" ) ;
#endif
2019-08-18 12:18:20 -04:00
var linkedPermissionSet = new [ ] { Permission . Banned , Permission . Flagged } ;
2019-04-02 21:20:37 -04:00
// if their permission level has been changed to level that needs to be updated on all accounts
2019-08-18 12:18:20 -04:00
if ( linkedPermissionSet . Contains ( newPermission ) | | linkedPermissionSet . Contains ( oldPermission ) )
2018-12-03 20:21:13 -05:00
{
2019-04-05 14:34:03 -04:00
//get all clients that have the same linkId
2019-04-02 21:20:37 -04:00
var iqMatchingClients = ctx . Clients
2019-04-05 14:34:03 -04:00
. Where ( _client = > _client . AliasLinkId = = entity . AliasLinkId ) ;
2019-04-02 21:20:37 -04:00
// this updates the level for all the clients with the same LinkId
// only if their new level is flagged or banned
2019-04-14 11:55:05 -04:00
await iqMatchingClients . ForEachAsync ( _client = >
2019-04-02 21:20:37 -04:00
{
2019-04-05 14:34:03 -04:00
_client . Level = newPermission ;
#if DEBUG = = true
temporalClient . CurrentServer . Logger . WriteDebug ( $"Updated linked {_client.ClientId} to {newPermission}" ) ;
#endif
} ) ;
2019-04-02 21:20:37 -04:00
2019-04-05 14:34:03 -04:00
await ctx . SaveChangesAsync ( ) ;
}
2017-11-25 20:29:58 -05:00
}
2019-04-02 21:20:37 -04:00
temporalClient . Level = newPermission ;
2017-11-25 20:29:58 -05:00
}
public async Task < EFClient > Delete ( EFClient entity )
{
2017-11-29 19:35:50 -05:00
using ( var context = new DatabaseContext ( ) )
2017-11-25 20:29:58 -05:00
{
2017-11-29 19:35:50 -05:00
var client = context . Clients
. Single ( e = > e . ClientId = = entity . ClientId ) ;
2017-11-25 20:29:58 -05:00
entity . Active = false ;
context . Entry ( entity ) . State = EntityState . Modified ;
await context . SaveChangesAsync ( ) ;
return entity ;
}
}
2019-06-11 09:00:14 -04:00
public Task < IList < EFClient > > Find ( Func < EFClient , bool > e )
2017-11-25 20:29:58 -05:00
{
2019-06-11 09:00:14 -04:00
throw new NotImplementedException ( ) ;
2017-11-25 20:29:58 -05:00
}
2019-11-15 15:50:20 -05:00
public async Task < EFClient > Get ( int entityId )
2017-11-25 20:29:58 -05:00
{
2019-11-15 15:50:20 -05:00
// todo: this needs to be optimized for large linked accounts
2018-09-13 15:34:42 -04:00
using ( var context = new DatabaseContext ( true ) )
2018-02-10 23:33:42 -05:00
{
2019-11-15 15:50:20 -05:00
var client = context . Clients
. Select ( _client = > new EFClient ( )
{
ClientId = _client . ClientId ,
AliasLinkId = _client . AliasLinkId ,
Level = _client . Level ,
Connections = _client . Connections ,
FirstConnection = _client . FirstConnection ,
LastConnection = _client . LastConnection ,
Masked = _client . Masked ,
NetworkId = _client . NetworkId ,
CurrentAlias = new EFAlias ( )
{
Name = _client . CurrentAlias . Name ,
IPAddress = _client . CurrentAlias . IPAddress
2019-11-25 13:05:12 -05:00
} ,
TotalConnectionTime = _client . TotalConnectionTime
2019-11-15 15:50:20 -05:00
} )
. FirstOrDefault ( _client = > _client . ClientId = = entityId ) ;
2019-11-18 10:25:39 -05:00
if ( client = = null )
{
return null ;
}
2019-11-15 15:50:20 -05:00
client . AliasLink = new EFAliasLink ( )
{
2019-11-18 09:08:09 -05:00
AliasLinkId = client . AliasLinkId ,
2019-11-15 15:50:20 -05:00
Children = await context . Aliases
. Where ( _alias = > _alias . LinkId = = client . AliasLinkId )
. Select ( _alias = > new EFAlias ( )
{
Name = _alias . Name ,
IPAddress = _alias . IPAddress
} ) . ToListAsync ( )
} ;
2019-10-07 11:26:07 -04:00
var foundClient = new
{
Client = client ,
LinkedAccounts = await context . Clients . Where ( _client = > _client . AliasLinkId = = client . AliasLinkId )
. Select ( _linkedClient = > new
{
_linkedClient . ClientId ,
_linkedClient . NetworkId
} )
. ToListAsync ( )
} ;
2018-05-14 13:55:10 -04:00
2018-05-24 15:48:57 -04:00
if ( foundClient = = null )
2018-11-25 21:00:36 -05:00
{
2018-05-24 15:48:57 -04:00
return null ;
2018-11-25 21:00:36 -05:00
}
2018-05-24 15:48:57 -04:00
2018-05-14 13:55:10 -04:00
foundClient . Client . LinkedAccounts = new Dictionary < int , long > ( ) ;
// todo: find out the best way to do this
// I'm doing this here because I don't know the best way to have multiple awaits in the query
foreach ( var linked in foundClient . LinkedAccounts )
2018-11-25 21:00:36 -05:00
{
2018-05-14 13:55:10 -04:00
foundClient . Client . LinkedAccounts . Add ( linked . ClientId , linked . NetworkId ) ;
2018-11-25 21:00:36 -05:00
}
2018-05-14 13:55:10 -04:00
return foundClient . Client ;
2018-02-10 23:33:42 -05:00
}
2017-11-25 20:29:58 -05:00
}
2018-09-13 15:34:42 -04:00
private static readonly Func < DatabaseContext , long , Task < EFClient > > _getUniqueQuery =
EF . CompileAsyncQuery ( ( DatabaseContext context , long networkId ) = >
context . Clients
2019-11-15 15:50:20 -05:00
. Select ( _client = > new EFClient ( )
{
ClientId = _client . ClientId ,
AliasLinkId = _client . AliasLinkId ,
Level = _client . Level ,
Connections = _client . Connections ,
FirstConnection = _client . FirstConnection ,
LastConnection = _client . LastConnection ,
Masked = _client . Masked ,
2019-11-25 13:05:12 -05:00
NetworkId = _client . NetworkId ,
2020-04-22 21:51:04 -04:00
TotalConnectionTime = _client . TotalConnectionTime ,
2020-04-29 18:05:36 -04:00
AliasLink = _client . AliasLink ,
Password = _client . Password ,
PasswordSalt = _client . PasswordSalt
2019-11-15 15:50:20 -05:00
} )
2018-09-13 15:34:42 -04:00
. FirstOrDefault ( c = > c . NetworkId = = networkId )
) ;
2020-04-22 19:46:41 -04:00
public virtual async Task < EFClient > GetUnique ( long entityAttribute )
2017-11-25 20:29:58 -05:00
{
2018-09-13 15:34:42 -04:00
using ( var context = new DatabaseContext ( true ) )
2017-11-25 20:29:58 -05:00
{
2018-09-13 15:34:42 -04:00
return await _getUniqueQuery ( context , entityAttribute ) ;
2017-11-25 20:29:58 -05:00
}
}
2019-04-02 21:20:37 -04:00
public async Task UpdateAlias ( EFClient temporalClient )
2017-11-25 20:29:58 -05:00
{
2017-11-29 19:35:50 -05:00
using ( var context = new DatabaseContext ( ) )
2017-11-25 20:29:58 -05:00
{
2019-04-02 21:20:37 -04:00
var entity = context . Clients
2017-11-29 19:35:50 -05:00
. Include ( c = > c . AliasLink )
2018-02-07 00:19:06 -05:00
. Include ( c = > c . CurrentAlias )
2019-04-02 21:20:37 -04:00
. First ( e = > e . ClientId = = temporalClient . ClientId ) ;
2018-09-11 15:28:37 -04:00
2019-04-02 21:20:37 -04:00
entity . CurrentServer = temporalClient . CurrentServer ;
2019-01-02 19:32:39 -05:00
2019-04-02 21:20:37 -04:00
await UpdateAlias ( temporalClient . Name , temporalClient . IPAddress , entity , context ) ;
2019-01-02 19:32:39 -05:00
2019-04-02 21:20:37 -04:00
temporalClient . CurrentAlias = entity . CurrentAlias ;
temporalClient . CurrentAliasId = entity . CurrentAliasId ;
temporalClient . AliasLink = entity . AliasLink ;
temporalClient . AliasLinkId = entity . AliasLinkId ;
2019-01-02 19:32:39 -05:00
}
}
2019-04-02 21:20:37 -04:00
public async Task < EFClient > Update ( EFClient temporalClient )
2019-01-02 19:32:39 -05:00
{
2019-12-27 21:37:50 -05:00
if ( temporalClient . ClientId < 1 )
{
temporalClient . CurrentServer ? . Logger . WriteDebug ( $"[update] {temporalClient} needs to be updated but they do not have a valid client id, ignoring.." ) ;
// note: we never do anything with the result of this so we can safely return null
return null ;
}
2019-01-02 19:32:39 -05:00
using ( var context = new DatabaseContext ( ) )
{
// grab the context version of the entity
2019-04-02 21:20:37 -04:00
var entity = context . Clients
. First ( client = > client . ClientId = = temporalClient . ClientId ) ;
2019-01-02 19:32:39 -05:00
2019-06-13 20:10:08 -04:00
if ( temporalClient . LastConnection > entity . LastConnection )
{
entity . LastConnection = temporalClient . LastConnection ;
}
if ( temporalClient . Connections > entity . Connections )
2019-06-12 11:27:15 -04:00
{
2019-06-13 20:10:08 -04:00
entity . Connections = temporalClient . Connections ;
2019-06-12 11:27:15 -04:00
}
2019-04-02 21:20:37 -04:00
entity . Masked = temporalClient . Masked ;
2019-06-13 20:10:08 -04:00
if ( temporalClient . TotalConnectionTime > entity . TotalConnectionTime )
{
entity . TotalConnectionTime = temporalClient . TotalConnectionTime ;
}
if ( temporalClient . Password ! = null )
{
entity . Password = temporalClient . Password ;
}
if ( temporalClient . PasswordSalt ! = null )
{
entity . PasswordSalt = temporalClient . PasswordSalt ;
}
2017-11-29 19:35:50 -05:00
// update in database
2017-11-25 20:29:58 -05:00
await context . SaveChangesAsync ( ) ;
2019-04-02 21:20:37 -04:00
return entity ;
2017-11-25 20:29:58 -05:00
}
}
2018-09-16 16:34:16 -04:00
#region ServiceSpecific
2017-11-25 20:29:58 -05:00
public async Task < IList < EFClient > > GetOwners ( )
{
2017-11-29 19:35:50 -05:00
using ( var context = new DatabaseContext ( ) )
2018-11-25 21:00:36 -05:00
{
2017-11-29 19:35:50 -05:00
return await context . Clients
2018-11-05 22:01:29 -05:00
. Where ( c = > c . Level = = Permission . Owner )
2017-11-29 19:35:50 -05:00
. ToListAsync ( ) ;
2018-11-25 21:00:36 -05:00
}
2017-11-25 20:29:58 -05:00
}
2019-03-24 22:34:20 -04:00
/// <summary>
/// retrieves the number of owners
/// (client level is owner)
/// </summary>
/// <returns></returns>
2020-05-16 12:54:01 -04:00
public virtual async Task < int > GetOwnerCount ( )
2019-03-24 22:34:20 -04:00
{
using ( var ctx = new DatabaseContext ( true ) )
{
2019-06-11 09:00:14 -04:00
return await ctx . Clients
2019-03-24 22:34:20 -04:00
. CountAsync ( _client = > _client . Level = = Permission . Owner ) ;
}
}
2019-08-02 19:04:34 -04:00
public async Task < EFClient > GetClientForLogin ( int clientId )
{
using ( var ctx = new DatabaseContext ( true ) )
{
return await ctx . Clients
. Select ( _client = > new EFClient ( )
{
NetworkId = _client . NetworkId ,
ClientId = _client . ClientId ,
CurrentAlias = new EFAlias ( )
{
Name = _client . CurrentAlias . Name
} ,
Password = _client . Password ,
PasswordSalt = _client . PasswordSalt ,
Level = _client . Level
} )
. FirstAsync ( _client = > _client . ClientId = = clientId ) ;
}
}
2019-05-03 21:13:51 -04:00
public async Task < List < EFClient > > GetPrivilegedClients ( bool includeName = true )
2018-02-16 23:24:03 -05:00
{
2018-09-13 15:34:42 -04:00
using ( var context = new DatabaseContext ( disableTracking : true ) )
2018-02-16 23:24:03 -05:00
{
2019-03-24 22:34:20 -04:00
var iqClients = from client in context . Clients . AsNoTracking ( )
2018-11-05 22:01:29 -05:00
where client . Level > = Permission . Trusted
2018-09-13 15:34:42 -04:00
where client . Active
2018-11-25 21:00:36 -05:00
select new EFClient ( )
2018-09-13 15:34:42 -04:00
{
2018-11-25 22:11:55 -05:00
AliasLinkId = client . AliasLinkId ,
2018-11-25 21:00:36 -05:00
CurrentAlias = client . CurrentAlias ,
2018-09-13 15:34:42 -04:00
ClientId = client . ClientId ,
2018-11-25 21:00:36 -05:00
Level = client . Level ,
Password = client . Password ,
2019-01-03 15:39:22 -05:00
PasswordSalt = client . PasswordSalt ,
2019-03-25 22:12:16 -04:00
NetworkId = client . NetworkId ,
LastConnection = client . LastConnection
2018-09-13 15:34:42 -04:00
} ;
#if DEBUG = = true
var clientsSql = iqClients . ToSql ( ) ;
#endif
2018-05-14 13:55:10 -04:00
return await iqClients . ToListAsync ( ) ;
2018-02-11 20:17:20 -05:00
}
2017-11-25 20:29:58 -05:00
}
2019-04-25 22:05:35 -04:00
public async Task < IList < PlayerInfo > > FindClientsByIdentifier ( string identifier )
2018-02-11 20:17:20 -05:00
{
2019-04-14 11:55:05 -04:00
if ( identifier ? . Length < 3 )
2018-10-15 20:51:04 -04:00
{
2019-04-25 22:05:35 -04:00
return new List < PlayerInfo > ( ) ;
2018-10-15 20:51:04 -04:00
}
2018-04-21 18:18:20 -04:00
2018-09-16 16:34:16 -04:00
using ( var context = new DatabaseContext ( disableTracking : true ) )
2018-09-11 15:28:37 -04:00
{
2019-05-03 21:13:51 -04:00
long? networkId = null ;
try
{
2020-01-15 19:43:52 -05:00
networkId = identifier . ConvertGuidToLong ( System . Globalization . NumberStyles . HexNumber ) ;
2019-05-03 21:13:51 -04:00
}
catch { }
2018-12-16 22:16:56 -05:00
int? ipAddress = identifier . ConvertToIP ( ) ;
2018-06-07 22:19:12 -04:00
2019-04-25 22:05:35 -04:00
IQueryable < EFAlias > iqLinkIds = context . Aliases . Where ( _alias = > _alias . Active ) ;
2018-09-11 15:28:37 -04:00
2019-04-25 22:05:35 -04:00
// we want to query for the IP ADdress
if ( ipAddress ! = null )
{
iqLinkIds = iqLinkIds . Where ( _alias = > _alias . IPAddress = = ipAddress ) ;
}
// want to find them by name (wildcard)
else
{
2019-08-02 19:04:34 -04:00
iqLinkIds = iqLinkIds . Where ( _alias = > EF . Functions . Like ( ( _alias . SearchableName ? ? _alias . Name . ToLower ( ) ) , $"%{identifier.ToLower()}%" ) ) ;
2019-04-25 22:05:35 -04:00
}
var linkIds = await iqLinkIds
. Select ( _alias = > _alias . LinkId )
. ToListAsync ( ) ;
2018-09-11 15:28:37 -04:00
2019-04-25 22:05:35 -04:00
// get all the clients that match the alias link or the network id
2018-09-11 15:28:37 -04:00
var iqClients = context . Clients
2019-04-25 22:05:35 -04:00
. Where ( _client = > _client . Active ) ;
2018-09-11 15:28:37 -04:00
2019-08-02 19:04:34 -04:00
2019-08-01 20:42:44 -04:00
iqClients = iqClients . Where ( _client = > networkId = = _client . NetworkId | | linkIds . Contains ( _client . AliasLinkId ) ) ;
2019-06-11 09:00:14 -04:00
2019-04-25 22:05:35 -04:00
// we want to project our results
var iqClientProjection = iqClients . OrderByDescending ( _client = > _client . LastConnection )
. Select ( _client = > new PlayerInfo ( )
{
Name = _client . CurrentAlias . Name ,
LevelInt = ( int ) _client . Level ,
LastConnection = _client . LastConnection ,
ClientId = _client . ClientId ,
} ) ;
2018-09-11 15:28:37 -04:00
#if DEBUG = = true
var iqClientsSql = iqClients . ToSql ( ) ;
#endif
2019-04-25 22:05:35 -04:00
var clients = await iqClientProjection . ToListAsync ( ) ;
2019-06-11 09:00:14 -04:00
2019-04-25 22:05:35 -04:00
// this is so we don't try to evaluate this in the linq to entities query
foreach ( var client in clients )
{
client . Level = ( ( Permission ) client . LevelInt ) . ToLocalizedLevelName ( ) ;
}
2018-02-23 02:06:13 -05:00
2019-04-25 22:05:35 -04:00
return clients ;
2018-02-23 02:06:13 -05:00
}
}
2017-11-25 20:29:58 -05:00
public async Task < int > GetTotalClientsAsync ( )
{
2018-09-16 16:34:16 -04:00
using ( var context = new DatabaseContext ( true ) )
2018-11-25 21:00:36 -05:00
{
2017-11-29 19:35:50 -05:00
return await context . Clients
. CountAsync ( ) ;
2018-11-25 21:00:36 -05:00
}
2017-11-25 20:29:58 -05:00
}
2019-07-16 16:27:19 -04:00
2019-07-24 11:36:37 -04:00
/// <summary>
/// Returns the number of clients seen today
/// </summary>
/// <returns></returns>
public async Task < int > GetRecentClientCount ( )
{
using ( var context = new DatabaseContext ( true ) )
{
var startOfPeriod = DateTime . UtcNow . AddHours ( - 24 ) ;
var iqQuery = context . Clients . Where ( _client = > _client . LastConnection > = startOfPeriod ) ;
#if DEBUG
string sql = iqQuery . ToSql ( ) ;
#endif
return await iqQuery . CountAsync ( ) ;
}
}
2019-07-16 16:27:19 -04:00
/// <summary>
/// gets the 10 most recently added clients to IW4MAdmin
/// </summary>
/// <returns></returns>
public async Task < IList < PlayerInfo > > GetRecentClients ( )
{
2019-10-02 18:59:10 -04:00
var startOfPeriod = DateTime . UtcNow . AddHours ( - 24 ) ;
2019-07-16 16:27:19 -04:00
using ( var context = new DatabaseContext ( true ) )
{
var iqClients = context . Clients
. Where ( _client = > _client . CurrentAlias . IPAddress ! = null )
2019-10-02 19:58:23 -04:00
. Where ( _client = > _client . FirstConnection > = startOfPeriod )
2019-10-07 11:26:07 -04:00
. OrderByDescending ( _client = > _client . FirstConnection )
2019-07-16 16:27:19 -04:00
. Select ( _client = > new PlayerInfo ( )
{
ClientId = _client . ClientId ,
Name = _client . CurrentAlias . Name ,
IPAddress = _client . CurrentAlias . IPAddress . ConvertIPtoString ( ) ,
2019-08-04 18:06:07 -04:00
LastConnection = _client . FirstConnection
2019-10-02 18:59:10 -04:00
} ) ;
2019-11-15 15:50:20 -05:00
2019-07-16 16:27:19 -04:00
#if DEBUG
var sql = iqClients . ToSql ( ) ;
#endif
return await iqClients . ToListAsync ( ) ;
}
}
2018-09-16 16:34:16 -04:00
#endregion
2019-08-08 16:30:06 -04:00
/// <summary>
2019-08-10 10:08:26 -04:00
/// retrieves the number of times the given client id has been reported
2019-08-08 16:30:06 -04:00
/// </summary>
/// <param name="clientId">client id to search for report counts of</param>
/// <returns></returns>
public async Task < int > GetClientReportCount ( int clientId )
{
using ( var ctx = new DatabaseContext ( true ) )
{
return await ctx . Penalties
. Where ( _penalty = > _penalty . Active )
. Where ( _penalty = > _penalty . OffenderId = = clientId )
. Where ( _penalty = > _penalty . Type = = EFPenalty . PenaltyType . Report )
. CountAsync ( ) ;
}
}
2019-08-10 10:08:26 -04:00
/// <summary>
/// indicates if the given clientid has been autoflagged
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public async Task < bool > IsAutoFlagged ( int clientId )
{
using ( var ctx = new DatabaseContext ( true ) )
{
var now = DateTime . UtcNow ;
return await ctx . Penalties
. Where ( _penalty = > _penalty . Active )
. Where ( _penalty = > _penalty . OffenderId = = clientId )
. Where ( _penalty = > _penalty . Type = = EFPenalty . PenaltyType . Flag )
. Where ( _penalty = > _penalty . PunisherId = = 1 )
. Where ( _penalty = > _penalty . Expires = = null | | _penalty . Expires > now )
. AnyAsync ( ) ;
}
}
2019-10-11 16:26:13 -04:00
/// <summary>
/// Unlinks shared GUID account into its own separate account
/// </summary>
/// <param name="clientId"></param>
/// <returns></returns>
public async Task UnlinkClient ( int clientId )
{
using ( var ctx = new DatabaseContext ( ) )
{
var newLink = new EFAliasLink ( ) { Active = true } ;
ctx . AliasLinks . Add ( newLink ) ;
await ctx . SaveChangesAsync ( ) ;
var client = await ctx . Clients . Include ( _client = > _client . CurrentAlias )
. FirstAsync ( _client = > _client . ClientId = = clientId ) ;
client . AliasLinkId = newLink . AliasLinkId ;
client . Level = Permission . User ;
await ctx . Aliases . Where ( _alias = > _alias . IPAddress = = client . IPAddress )
. ForEachAsync ( _alias = > _alias . LinkId = newLink . AliasLinkId ) ;
await ctx . SaveChangesAsync ( ) ;
}
}
2020-05-24 22:22:26 -04:00
/// <summary>
/// find clients matching the given query
/// </summary>
/// <param name="query">query filters</param>
/// <returns></returns>
public async Task < ResourceQueryHelperResult < FindClientResult > > QueryResource ( FindClientRequest query )
{
var result = new ResourceQueryHelperResult < FindClientResult > ( ) ;
using var context = _contextFactory . CreateContext ( enableTracking : false ) ;
IQueryable < EFClient > iqClients = null ;
if ( ! string . IsNullOrEmpty ( query . Xuid ) )
{
long networkId = query . Xuid . ConvertGuidToLong ( System . Globalization . NumberStyles . HexNumber ) ;
iqClients = context . Clients . Where ( _client = > _client . NetworkId = = networkId ) ;
}
else if ( ! string . IsNullOrEmpty ( query . Name ) )
{
iqClients = context . Clients . Where ( _client = > EF . Functions . Like ( _client . CurrentAlias . Name . ToLower ( ) , $"%{query.Name.ToLower()}%" ) ) ;
}
if ( query . Direction = = SortDirection . Ascending )
{
iqClients = iqClients . OrderBy ( _client = > _client . LastConnection ) ;
}
else
{
iqClients = iqClients . OrderByDescending ( _client = > _client . LastConnection ) ;
}
var queryResults = await iqClients
. Select ( _client = > new FindClientResult
{
ClientId = _client . ClientId ,
Xuid = _client . NetworkId . ToString ( "X" ) ,
Name = _client . CurrentAlias . Name
} )
. Skip ( query . Offset )
. Take ( query . Count )
. ToListAsync ( ) ;
result . TotalResultCount = await iqClients . CountAsync ( ) ;
result . Results = queryResults ;
result . RetrievedResultCount = queryResults . Count ;
return result ;
}
2017-11-25 20:29:58 -05:00
}
}