Merge pull request #128 from RaidMax/feature/issue-126-create-run-as-command
Feature/issue 126 create run as command
This commit is contained in:
commit
f5b0167f81
@ -431,7 +431,8 @@ namespace IW4MAdmin.Application
|
||||
{
|
||||
Name = cmd.Name,
|
||||
Alias = cmd.Alias,
|
||||
MinimumPermission = cmd.Permission
|
||||
MinimumPermission = cmd.Permission,
|
||||
AllowImpersonation = cmd.AllowImpersonation
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -144,6 +144,7 @@ namespace IW4MAdmin
|
||||
catch (CommandException e)
|
||||
{
|
||||
Logger.WriteInfo(e.Message);
|
||||
E.FailReason = GameEvent.EventFailReason.Invalid;
|
||||
}
|
||||
|
||||
if (C != null)
|
||||
@ -342,7 +343,7 @@ namespace IW4MAdmin
|
||||
return false;
|
||||
}
|
||||
|
||||
if (E.Origin.Level > EFClient.Permission.Moderator)
|
||||
if (E.Origin.Level > Permission.Moderator)
|
||||
{
|
||||
E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
|
||||
}
|
||||
@ -371,7 +372,7 @@ namespace IW4MAdmin
|
||||
Expires = expires,
|
||||
Offender = E.Target,
|
||||
Offense = E.Data,
|
||||
Punisher = E.Origin,
|
||||
Punisher = E.ImpersonationOrigin ?? E.Origin,
|
||||
When = DateTime.UtcNow,
|
||||
Link = E.Target.AliasLink
|
||||
};
|
||||
@ -388,7 +389,7 @@ namespace IW4MAdmin
|
||||
Expires = DateTime.UtcNow,
|
||||
Offender = E.Target,
|
||||
Offense = E.Data,
|
||||
Punisher = E.Origin,
|
||||
Punisher = E.ImpersonationOrigin ?? E.Origin,
|
||||
When = DateTime.UtcNow,
|
||||
Link = E.Target.AliasLink
|
||||
};
|
||||
@ -413,7 +414,7 @@ namespace IW4MAdmin
|
||||
Expires = DateTime.UtcNow,
|
||||
Offender = E.Target,
|
||||
Offense = E.Message,
|
||||
Punisher = E.Origin,
|
||||
Punisher = E.ImpersonationOrigin ?? E.Origin,
|
||||
Active = true,
|
||||
When = DateTime.UtcNow,
|
||||
Link = E.Target.AliasLink
|
||||
@ -432,28 +433,28 @@ namespace IW4MAdmin
|
||||
|
||||
else if (E.Type == GameEvent.EventType.TempBan)
|
||||
{
|
||||
await TempBan(E.Data, (TimeSpan)E.Extra, E.Target, E.Origin); ;
|
||||
await TempBan(E.Data, (TimeSpan)E.Extra, E.Target, E.ImpersonationOrigin ?? E.Origin); ;
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Ban)
|
||||
{
|
||||
bool isEvade = E.Extra != null ? (bool)E.Extra : false;
|
||||
await Ban(E.Data, E.Target, E.Origin, isEvade);
|
||||
await Ban(E.Data, E.Target, E.ImpersonationOrigin ?? E.Origin, isEvade);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Unban)
|
||||
{
|
||||
await Unban(E.Data, E.Target, E.Origin);
|
||||
await Unban(E.Data, E.Target, E.ImpersonationOrigin ?? E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Kick)
|
||||
{
|
||||
await Kick(E.Data, E.Target, E.Origin);
|
||||
await Kick(E.Data, E.Target, E.ImpersonationOrigin ?? E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Warn)
|
||||
{
|
||||
await Warn(E.Data, E.Target, E.Origin);
|
||||
await Warn(E.Data, E.Target, E.ImpersonationOrigin ?? E.Origin);
|
||||
}
|
||||
|
||||
else if (E.Type == GameEvent.EventType.Disconnect)
|
||||
|
@ -9,6 +9,7 @@ using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Exceptions;
|
||||
using SharedLibraryCore.Helpers;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using SharedLibraryCore.Repositories;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@ -284,6 +285,7 @@ namespace IW4MAdmin.Application
|
||||
.AddSingleton<IConfigurationHandlerFactory, ConfigurationHandlerFactory>()
|
||||
.AddSingleton<IParserRegexFactory, ParserRegexFactory>()
|
||||
.AddSingleton<IDatabaseContextFactory, DatabaseContextFactory>()
|
||||
.AddSingleton<IAuditInformationRepository, AuditInformationRepository>()
|
||||
.AddTransient<IParserPatternMatcher, ParserPatternMatcher>()
|
||||
.AddSingleton(_serviceProvider =>
|
||||
{
|
||||
|
@ -16,9 +16,23 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
|
||||
{
|
||||
OffensiveWords = new List<string>()
|
||||
{
|
||||
@"\s*n+.*i+.*g+.*e+.*r+\s*",
|
||||
@"\s*n+.*i+.*g+.*a+\s*",
|
||||
@"\s*f+u+.*c+.*k+.*\s*"
|
||||
@"(ph|f)[a@]g[s\$]?",
|
||||
@"(ph|f)[a@]gg[i1]ng",
|
||||
@"(ph|f)[a@]gg?[o0][t\+][s\$]?",
|
||||
@"(ph|f)[a@]gg[s\$]",
|
||||
@"(ph|f)[e3][l1][l1]?[a@][t\+][i1][o0]",
|
||||
@"(ph|f)u(c|k|ck|q)",
|
||||
@"(ph|f)u(c|k|ck|q)[s\$]?",
|
||||
@"(c|k|ck|q)un[t\+][l1][i1](c|k|ck|q)",
|
||||
@"(c|k|ck|q)un[t\+][l1][i1](c|k|ck|q)[e3]r",
|
||||
@"(c|k|ck|q)un[t\+][l1][i1](c|k|ck|q)[i1]ng",
|
||||
@"b[i1][t\+]ch[s\$]?",
|
||||
@"b[i1][t\+]ch[e3]r[s\$]?",
|
||||
@"b[i1][t\+]ch[e3][s\$]",
|
||||
@"b[i1][t\+]ch[i1]ng?",
|
||||
@"n[i1]gg?[e3]r[s\$]?",
|
||||
@"[s\$]h[i1][t\+][s\$]?",
|
||||
@"[s\$][l1]u[t\+][s\$]?"
|
||||
};
|
||||
|
||||
var loc = Utilities.CurrentLocalization.LocalizationIndex;
|
||||
|
@ -19,6 +19,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
|
||||
Alias = "rs";
|
||||
Permission = EFClient.Permission.User;
|
||||
RequiresTarget = false;
|
||||
//AllowImpersonation = true;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
|
@ -117,5 +117,10 @@ namespace SharedLibraryCore
|
||||
/// Argument list for the command
|
||||
/// </summary>
|
||||
public CommandArgument[] Arguments { get; protected set; } = new CommandArgument[0];
|
||||
|
||||
/// <summary>
|
||||
/// indicates if this command allows impersonation (run as)
|
||||
/// </summary>
|
||||
public bool AllowImpersonation { get; set; }
|
||||
}
|
||||
}
|
||||
|
13
SharedLibraryCore/Commands/CommandExtensions.cs
Normal file
13
SharedLibraryCore/Commands/CommandExtensions.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
{
|
||||
public static class CommandExtensions
|
||||
{
|
||||
public static bool IsTargetingSelf(this GameEvent gameEvent) => gameEvent.Origin?.Equals(gameEvent.Target) ?? false;
|
||||
|
||||
public static bool CanPerformActionOnTarget(this GameEvent gameEvent) => gameEvent.Origin?.Level > gameEvent.Target?.Level;
|
||||
}
|
||||
}
|
@ -21,7 +21,8 @@ namespace SharedLibraryCore.Commands
|
||||
Command C = null;
|
||||
foreach (Command cmd in Manager.GetCommands())
|
||||
{
|
||||
if (cmd.Name == CommandString.ToLower() || cmd.Alias == CommandString.ToLower())
|
||||
if (cmd.Name.Equals(CommandString, StringComparison.OrdinalIgnoreCase) ||
|
||||
(cmd.Alias ?? "").Equals(CommandString, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
C = cmd;
|
||||
}
|
||||
@ -33,6 +34,12 @@ namespace SharedLibraryCore.Commands
|
||||
throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\"");
|
||||
}
|
||||
|
||||
if (!C.AllowImpersonation && E.ImpersonationOrigin != null)
|
||||
{
|
||||
E.ImpersonationOrigin.Tell(loc["COMMANDS_RUN_AS_FAIL"]);
|
||||
throw new CommandException($"Command {C.Name} cannot be run as another client");
|
||||
}
|
||||
|
||||
E.Data = E.Data.RemoveWords(1);
|
||||
String[] Args = E.Data.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
// todo: the code below can be cleaned up
|
||||
|
@ -1434,6 +1434,7 @@ namespace SharedLibraryCore.Commands
|
||||
Alias = "sp";
|
||||
Permission = Permission.Moderator;
|
||||
RequiresTarget = false;
|
||||
AllowImpersonation = true;
|
||||
Arguments = new[]
|
||||
{
|
||||
new CommandArgument()
|
||||
|
65
SharedLibraryCore/Commands/RunAsCommand.cs
Normal file
65
SharedLibraryCore/Commands/RunAsCommand.cs
Normal file
@ -0,0 +1,65 @@
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Commands
|
||||
{
|
||||
public class RunAsCommand : Command
|
||||
{
|
||||
public RunAsCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
|
||||
{
|
||||
Name = "runas";
|
||||
Description = lookup["COMMANDS_RUN_AS_DESC"];
|
||||
Alias = "ra";
|
||||
Permission = EFClient.Permission.Moderator;
|
||||
RequiresTarget = true;
|
||||
Arguments = new[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
Name = lookup["COMMANDS_ARGS_COMMANDS"],
|
||||
Required = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if (E.IsTargetingSelf())
|
||||
{
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SELF"]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!E.CanPerformActionOnTarget())
|
||||
{
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_FAIL_PERM"]);
|
||||
return;
|
||||
}
|
||||
|
||||
string cmd = $"{Utilities.CommandPrefix}{E.Data}";
|
||||
var impersonatedCommandEvent = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Command,
|
||||
Origin = E.Target,
|
||||
ImpersonationOrigin = E.Origin,
|
||||
Message = cmd,
|
||||
Data = cmd,
|
||||
Owner = E.Owner
|
||||
};
|
||||
E.Owner.Manager.GetEventHandler().AddEvent(impersonatedCommandEvent);
|
||||
|
||||
var result = await impersonatedCommandEvent.WaitAsync(Utilities.DefaultCommandTimeout, E.Owner.Manager.CancellationToken);
|
||||
var response = E.Owner.CommandResult.Where(c => c.ClientId == E.Target.ClientId).ToList();
|
||||
|
||||
// remove the added command response
|
||||
for (int i = 0; i < response.Count; i++)
|
||||
{
|
||||
E.Origin.Tell(_translationLookup["COMMANDS_RUN_AS_SUCCESS"].FormatExt(response[i].Response));
|
||||
E.Owner.CommandResult.Remove(response[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,5 +24,10 @@ namespace SharedLibraryCore.Configuration
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public Permission MinimumPermission { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the command can be run by another user (impersonation)
|
||||
/// </summary>
|
||||
public bool AllowImpersonation { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -71,8 +71,8 @@ namespace SharedLibraryCore.Database
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
// optionsBuilder.UseLoggerFactory(_loggerFactory)
|
||||
// .EnableSensitiveDataLogging();
|
||||
optionsBuilder.UseLoggerFactory(_loggerFactory)
|
||||
.EnableSensitiveDataLogging();
|
||||
|
||||
if (string.IsNullOrEmpty(_ConnectionString))
|
||||
{
|
||||
|
@ -1,8 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using System.Text;
|
||||
|
||||
namespace SharedLibraryCore.Database.Models
|
||||
{
|
||||
@ -22,6 +19,7 @@ namespace SharedLibraryCore.Database.Models
|
||||
public int ChangeHistoryId { get; set; }
|
||||
public int OriginEntityId { get; set; }
|
||||
public int TargetEntityId { get; set; }
|
||||
public int? ImpersonationEntityId { get; set; }
|
||||
public ChangeType TypeOfChange { get; set; }
|
||||
public DateTime TimeChanged { get; set; } = DateTime.UtcNow;
|
||||
[MaxLength(128)]
|
||||
|
57
SharedLibraryCore/Dtos/AuditInfo.cs
Normal file
57
SharedLibraryCore/Dtos/AuditInfo.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
/// <summary>
|
||||
/// data transfer class for audit information
|
||||
/// </summary>
|
||||
public class AuditInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// name of the origin entity
|
||||
/// </summary>
|
||||
public string OriginName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// id of the origin entity
|
||||
/// </summary>
|
||||
public int OriginId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// name of the target entity
|
||||
/// </summary>
|
||||
public string TargetName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// id of the target entity
|
||||
/// </summary>
|
||||
public int? TargetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// when the audit event occured
|
||||
/// </summary>
|
||||
public DateTime When { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// what audit action occured
|
||||
/// </summary>
|
||||
public string Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// additional comment data about the audit event
|
||||
/// </summary>
|
||||
public string Data { get; set; }
|
||||
|
||||
private string oldValue;
|
||||
/// <summary>
|
||||
/// previous value
|
||||
/// </summary>
|
||||
public string OldValue { get => oldValue ?? "--"; set => oldValue = value; }
|
||||
|
||||
private string newValue;
|
||||
/// <summary>
|
||||
/// new value
|
||||
/// </summary>
|
||||
public string NewValue { get => newValue ?? "--"; set => newValue = value; }
|
||||
}
|
||||
}
|
34
SharedLibraryCore/Dtos/PaginationInfo.cs
Normal file
34
SharedLibraryCore/Dtos/PaginationInfo.cs
Normal file
@ -0,0 +1,34 @@
|
||||
namespace SharedLibraryCore.Dtos
|
||||
{
|
||||
/// <summary>
|
||||
/// pagination information holder class
|
||||
/// </summary>
|
||||
public class PaginationInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// how many items to skip
|
||||
/// </summary>
|
||||
public int Offset { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// how many itesm to take
|
||||
/// </summary>
|
||||
public int Count { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// filter query
|
||||
/// </summary>
|
||||
public string Filter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// direction of ordering
|
||||
/// </summary>
|
||||
public SortDirection Direction { get; set; } = SortDirection.Descending;
|
||||
}
|
||||
|
||||
public enum SortDirection
|
||||
{
|
||||
Ascending,
|
||||
Descending
|
||||
}
|
||||
}
|
@ -227,6 +227,7 @@ namespace SharedLibraryCore
|
||||
public int? GameTime { get; set; }
|
||||
public EFClient Origin;
|
||||
public EFClient Target;
|
||||
public EFClient ImpersonationOrigin { get; set; }
|
||||
public Server Owner;
|
||||
public bool IsRemote { get; set; } = false;
|
||||
public object Extra { get; set; }
|
||||
@ -276,7 +277,7 @@ namespace SharedLibraryCore
|
||||
Owner?.Logger.WriteError("Waiting for event to complete timed out");
|
||||
Owner?.Logger.WriteDebug($"{Id}, {Type}, {Data}, {Extra}, {FailReason.ToString()}, {Message}, {Origin}, {Target}");
|
||||
#if DEBUG
|
||||
throw new Exception();
|
||||
//throw new Exception();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
19
SharedLibraryCore/Interfaces/IAuditInformationRepository.cs
Normal file
19
SharedLibraryCore/Interfaces/IAuditInformationRepository.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using SharedLibraryCore.Dtos;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// describes the capabilities of the audit info repository
|
||||
/// </summary>
|
||||
public interface IAuditInformationRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// retrieves a list of audit information for given pagination params
|
||||
/// </summary>
|
||||
/// <param name="paginationInfo">pagination info</param>
|
||||
/// <returns></returns>
|
||||
Task<IList<AuditInfo>> ListAuditInformation(PaginationInfo paginationInfo);
|
||||
}
|
||||
}
|
@ -44,5 +44,10 @@ namespace SharedLibraryCore.Interfaces
|
||||
/// Indicates if target is required
|
||||
/// </summary>
|
||||
bool RequiresTarget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the commands can be run as another client
|
||||
/// </summary>
|
||||
bool AllowImpersonation { get; }
|
||||
}
|
||||
}
|
||||
|
919
SharedLibraryCore/Migrations/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs
generated
Normal file
919
SharedLibraryCore/Migrations/20200423225137_AddImpersonationIdToEFChangeHistory.Designer.cs
generated
Normal file
@ -0,0 +1,919 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using SharedLibraryCore.Database;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20200423225137_AddImpersonationIdToEFChangeHistory")]
|
||||
partial class AddImpersonationIdToEFChangeHistory
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.3");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.Property<int>("SnapshotId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CurrentSessionLength")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("CurrentStrain")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("CurrentViewAngleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Deaths")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Distance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("EloRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HitDestinationId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitLocation")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitOriginId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Hits")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Kills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LastStrainAngleId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("RecoilOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionAngleOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionAverageSnapValue")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SessionSPM")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SessionScore")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SessionSnapHits")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("StrainAngleBetween")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("TimeSinceLastEvent")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("WeaponId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("SnapshotId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("CurrentViewAngleId");
|
||||
|
||||
b.HasIndex("HitDestinationId");
|
||||
|
||||
b.HasIndex("HitOriginId");
|
||||
|
||||
b.HasIndex("LastStrainAngleId");
|
||||
|
||||
b.ToTable("EFACSnapshot");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.Property<int>("ACSnapshotVector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SnapshotId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Vector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ACSnapshotVector3Id");
|
||||
|
||||
b.HasIndex("SnapshotId");
|
||||
|
||||
b.HasIndex("Vector3Id");
|
||||
|
||||
b.ToTable("EFACSnapshotVector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.Property<long>("KillId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AttackerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Damage")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("DeathOriginVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("DeathType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Fraction")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("HitLoc")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("IsKill")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("KillOriginVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Map")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("VictimId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("ViewAnglesVector3Id")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("VisibilityPercentage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Weapon")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("KillId");
|
||||
|
||||
b.HasIndex("AttackerId");
|
||||
|
||||
b.HasIndex("DeathOriginVector3Id");
|
||||
|
||||
b.HasIndex("KillOriginVector3Id");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("VictimId");
|
||||
|
||||
b.HasIndex("ViewAnglesVector3Id");
|
||||
|
||||
b.ToTable("EFClientKills");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.Property<long>("MessageId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Message")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TimeSent")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("MessageId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("TimeSent");
|
||||
|
||||
b.ToTable("EFClientMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.ToTable("EFClientRatingHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("AverageRecoilOffset")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("AverageSnapValue")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Deaths")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("EloRating")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Kills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("MaxStrain")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("RollingWeightedKDR")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("SPM")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<double>("Skill")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("SnapHitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("TimePlayed")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("VisionAverage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("ClientId", "ServerId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFClientStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.Property<int>("HitLocationCountId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("EFClientStatisticsClientId")
|
||||
.HasColumnName("EFClientStatisticsClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("EFClientStatisticsServerId")
|
||||
.HasColumnName("EFClientStatisticsServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("HitCount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("HitOffsetAverage")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Location")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("MaxAngleDistance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("HitLocationCountId");
|
||||
|
||||
b.HasIndex("EFClientStatisticsServerId");
|
||||
|
||||
b.HasIndex("EFClientStatisticsClientId", "EFClientStatisticsServerId");
|
||||
|
||||
b.ToTable("EFHitLocationCounts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.Property<int>("RatingId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ActivityAmount")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Newest")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<double>("Performance")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<int>("Ranking")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("RatingHistoryId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long?>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("RatingId");
|
||||
|
||||
b.HasIndex("RatingHistoryId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.HasIndex("Performance", "Ranking", "When");
|
||||
|
||||
b.ToTable("EFRating");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServer", b =>
|
||||
{
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("EndPoint")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("GameName")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Port")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ServerId");
|
||||
|
||||
b.ToTable("EFServers");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.Property<int>("StatisticId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("ServerId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("TotalKills")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("TotalPlayTime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("StatisticId");
|
||||
|
||||
b.HasIndex("ServerId");
|
||||
|
||||
b.ToTable("EFServerStatistics");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.Property<int>("AliasId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("DateAdded")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("IPAddress")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.Property<string>("SearchableName")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(24);
|
||||
|
||||
b.HasKey("AliasId");
|
||||
|
||||
b.HasIndex("IPAddress");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("Name");
|
||||
|
||||
b.HasIndex("SearchableName");
|
||||
|
||||
b.HasIndex("Name", "IPAddress")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFAlias");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAliasLink", b =>
|
||||
{
|
||||
b.Property<int>("AliasLinkId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("AliasLinkId");
|
||||
|
||||
b.ToTable("EFAliasLinks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFChangeHistory", b =>
|
||||
{
|
||||
b.Property<int>("ChangeHistoryId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Comment")
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(128);
|
||||
|
||||
b.Property<string>("CurrentValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ImpersonationEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OriginEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("PreviousValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TargetEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("TimeChanged")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TypeOfChange")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ChangeHistoryId");
|
||||
|
||||
b.ToTable("EFChangeHistory");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.Property<int>("ClientId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AliasLinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Connections")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("CurrentAliasId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("FirstConnection")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("LastConnection")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Level")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Masked")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<long>("NetworkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Password")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("PasswordSalt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("TotalConnectionTime")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("ClientId");
|
||||
|
||||
b.HasIndex("AliasLinkId");
|
||||
|
||||
b.HasIndex("CurrentAliasId");
|
||||
|
||||
b.HasIndex("NetworkId")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("EFClients");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.Property<int>("MetaId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("ClientId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Extra")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasMaxLength(32);
|
||||
|
||||
b.Property<DateTime>("Updated")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("Value")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("MetaId");
|
||||
|
||||
b.HasIndex("ClientId");
|
||||
|
||||
b.HasIndex("Key");
|
||||
|
||||
b.ToTable("EFMeta");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.Property<int>("PenaltyId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<bool>("Active")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("AutomatedOffense")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime?>("Expires")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<bool>("IsEvadedOffense")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("LinkId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OffenderId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Offense")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PunisherId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<DateTime>("When")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("PenaltyId");
|
||||
|
||||
b.HasIndex("LinkId");
|
||||
|
||||
b.HasIndex("OffenderId");
|
||||
|
||||
b.HasIndex("PunisherId");
|
||||
|
||||
b.ToTable("EFPenalties");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Helpers.Vector3", b =>
|
||||
{
|
||||
b.Property<int>("Vector3Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<float>("X")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<float>("Y")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.Property<float>("Z")
|
||||
.HasColumnType("REAL");
|
||||
|
||||
b.HasKey("Vector3Id");
|
||||
|
||||
b.ToTable("Vector3");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "CurrentViewAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentViewAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitDestination")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitDestinationId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "HitOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("HitOriginId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "LastStrainAngle")
|
||||
.WithMany()
|
||||
.HasForeignKey("LastStrainAngleId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshotVector3", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", "Snapshot")
|
||||
.WithMany("PredictedViewAngles")
|
||||
.HasForeignKey("SnapshotId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "Vector")
|
||||
.WithMany()
|
||||
.HasForeignKey("Vector3Id")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientKill", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Attacker")
|
||||
.WithMany()
|
||||
.HasForeignKey("AttackerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "DeathOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("DeathOriginVector3Id");
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "KillOrigin")
|
||||
.WithMany()
|
||||
.HasForeignKey("KillOriginVector3Id");
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Victim")
|
||||
.WithMany()
|
||||
.HasForeignKey("VictimId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Helpers.Vector3", "ViewAngles")
|
||||
.WithMany()
|
||||
.HasForeignKey("ViewAnglesVector3Id");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientMessage", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFHitLocationCount", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany()
|
||||
.HasForeignKey("EFClientStatisticsClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientStatistics", null)
|
||||
.WithMany("HitLocations")
|
||||
.HasForeignKey("EFClientStatisticsClientId", "EFClientStatisticsServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFRating", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFClientRatingHistory", "RatingHistory")
|
||||
.WithMany("Ratings")
|
||||
.HasForeignKey("RatingHistoryId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFServerStatistics", b =>
|
||||
{
|
||||
b.HasOne("IW4MAdmin.Plugins.Stats.Models.EFServer", "Server")
|
||||
.WithMany()
|
||||
.HasForeignKey("ServerId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFAlias", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("Children")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFClient", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "AliasLink")
|
||||
.WithMany()
|
||||
.HasForeignKey("AliasLinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAlias", "CurrentAlias")
|
||||
.WithMany()
|
||||
.HasForeignKey("CurrentAliasId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFMeta", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Client")
|
||||
.WithMany("Meta")
|
||||
.HasForeignKey("ClientId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
});
|
||||
|
||||
modelBuilder.Entity("SharedLibraryCore.Database.Models.EFPenalty", b =>
|
||||
{
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFAliasLink", "Link")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("LinkId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Offender")
|
||||
.WithMany("ReceivedPenalties")
|
||||
.HasForeignKey("OffenderId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.HasOne("SharedLibraryCore.Database.Models.EFClient", "Punisher")
|
||||
.WithMany("AdministeredPenalties")
|
||||
.HasForeignKey("PunisherId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
public partial class AddImpersonationIdToEFChangeHistory : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "ImpersonationEntityId",
|
||||
table: "EFChangeHistory",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "ImpersonationEntityId",
|
||||
table: "EFChangeHistory");
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace SharedLibraryCore.Migrations
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
.HasAnnotation("ProductVersion", "3.1.3");
|
||||
|
||||
modelBuilder.Entity("IW4MAdmin.Plugins.Stats.Models.EFACSnapshot", b =>
|
||||
{
|
||||
@ -511,6 +511,9 @@ namespace SharedLibraryCore.Migrations
|
||||
b.Property<string>("CurrentValue")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("ImpersonationEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("OriginEntityId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
55
SharedLibraryCore/Repositories/AuditInformationRepository.cs
Normal file
55
SharedLibraryCore/Repositories/AuditInformationRepository.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SharedLibraryCore.Repositories
|
||||
{
|
||||
/// <summary>
|
||||
/// implementation if IAuditInformationRepository
|
||||
/// </summary>
|
||||
public class AuditInformationRepository : IAuditInformationRepository
|
||||
{
|
||||
private readonly IDatabaseContextFactory _contextFactory;
|
||||
|
||||
public AuditInformationRepository(IDatabaseContextFactory contextFactory)
|
||||
{
|
||||
_contextFactory = contextFactory;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<IList<AuditInfo>> ListAuditInformation(PaginationInfo paginationInfo)
|
||||
{
|
||||
using (var ctx = _contextFactory.CreateContext(enableTracking: false))
|
||||
{
|
||||
var iqItems = (from change in ctx.EFChangeHistory
|
||||
where change.TypeOfChange != Database.Models.EFChangeHistory.ChangeType.Ban
|
||||
orderby change.TimeChanged descending
|
||||
join originClient in ctx.Clients
|
||||
on (change.ImpersonationEntityId ?? change.OriginEntityId) equals originClient.ClientId
|
||||
join targetClient in ctx.Clients
|
||||
on change.TargetEntityId equals targetClient.ClientId
|
||||
into targetChange
|
||||
from targetClient in targetChange.DefaultIfEmpty()
|
||||
select new AuditInfo()
|
||||
{
|
||||
Action = change.TypeOfChange.ToString(),
|
||||
OriginName = originClient.CurrentAlias.Name,
|
||||
OriginId = originClient.ClientId,
|
||||
TargetName = targetClient == null ? "" : targetClient.CurrentAlias.Name,
|
||||
TargetId = targetClient == null ? new int?() : targetClient.ClientId,
|
||||
When = change.TimeChanged,
|
||||
Data = change.Comment,
|
||||
OldValue = change.PreviousValue,
|
||||
NewValue = change.CurrentValue
|
||||
})
|
||||
.Skip(paginationInfo.Offset)
|
||||
.Take(paginationInfo.Count);
|
||||
|
||||
return await iqItems.ToListAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
OriginEntityId = e.Origin.ClientId,
|
||||
TargetEntityId = e.Target.ClientId,
|
||||
ImpersonationEntityId = e.ImpersonationOrigin?.ClientId,
|
||||
TypeOfChange = EFChangeHistory.ChangeType.Ban,
|
||||
Comment = e.Data
|
||||
};
|
||||
@ -43,6 +44,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
OriginEntityId = e.Origin.ClientId,
|
||||
TargetEntityId = e.Target?.ClientId ?? 0,
|
||||
ImpersonationEntityId = e.ImpersonationOrigin?.ClientId,
|
||||
Comment = "Executed command",
|
||||
CurrentValue = e.Message,
|
||||
TypeOfChange = EFChangeHistory.ChangeType.Command
|
||||
@ -53,6 +55,7 @@ namespace SharedLibraryCore.Services
|
||||
{
|
||||
OriginEntityId = e.Origin.ClientId,
|
||||
TargetEntityId = e.Target.ClientId,
|
||||
ImpersonationEntityId = e.ImpersonationOrigin?.ClientId,
|
||||
Comment = "Changed permission level",
|
||||
TypeOfChange = EFChangeHistory.ChangeType.Permission,
|
||||
CurrentValue = ((EFClient.Permission)e.Extra).ToString()
|
||||
|
@ -32,9 +32,9 @@ namespace SharedLibraryCore
|
||||
#endif
|
||||
public static Encoding EncodingType;
|
||||
public static Localization.Layout CurrentLocalization = new Localization.Layout(new Dictionary<string, string>());
|
||||
public static TimeSpan DefaultCommandTimeout = new TimeSpan(0, 0, 25);
|
||||
public static TimeSpan DefaultCommandTimeout { get; set; } = new TimeSpan(0, 0, 25);
|
||||
public static char[] DirectorySeparatorChars = new[] { '\\', '/' };
|
||||
|
||||
public static char CommandPrefix { get; set; } = '!';
|
||||
public static EFClient IW4MAdminClient(Server server = null)
|
||||
{
|
||||
return new EFClient()
|
||||
|
178
Tests/ApplicationTests/CommandTests.cs
Normal file
178
Tests/ApplicationTests/CommandTests.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using IW4MAdmin;
|
||||
using FakeItEasy;
|
||||
using IW4MAdmin.Application.EventParsers;
|
||||
using System.Linq;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using IW4MAdmin.Application.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Config;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibraryCore.Database.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using ApplicationTests.Fixtures;
|
||||
using System.Threading.Tasks;
|
||||
using SharedLibraryCore.Commands;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore;
|
||||
using ApplicationTests.Mocks;
|
||||
|
||||
namespace ApplicationTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CommandTests
|
||||
{
|
||||
ILogger logger;
|
||||
private IServiceProvider serviceProvider;
|
||||
private ITranslationLookup transLookup;
|
||||
private CommandConfiguration cmdConfig;
|
||||
private MockEventHandler mockEventHandler;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
logger = A.Fake<ILogger>();
|
||||
cmdConfig = new CommandConfiguration();
|
||||
|
||||
serviceProvider = new ServiceCollection()
|
||||
.BuildBase()
|
||||
.BuildServiceProvider();
|
||||
|
||||
mockEventHandler = new MockEventHandler(true);
|
||||
A.CallTo(() => serviceProvider.GetRequiredService<IManager>().GetEventHandler())
|
||||
.Returns(mockEventHandler);
|
||||
|
||||
var mgr = serviceProvider.GetRequiredService<IManager>();
|
||||
transLookup = serviceProvider.GetRequiredService<ITranslationLookup>();
|
||||
|
||||
A.CallTo(() => mgr.GetCommands())
|
||||
.Returns(new Command[]
|
||||
{
|
||||
new ImpersonatableCommand(cmdConfig, transLookup),
|
||||
new NonImpersonatableCommand(cmdConfig, transLookup)
|
||||
});
|
||||
|
||||
//Utilities.DefaultCommandTimeout = new TimeSpan(0, 0, 2);
|
||||
}
|
||||
|
||||
#region RUNAS
|
||||
[Test]
|
||||
public async Task Test_RunAsFailsOnSelf()
|
||||
{
|
||||
var cmd = new RunAsCommand(cmdConfig, transLookup);
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
var target = ClientGenerators.CreateBasicClient(server);
|
||||
|
||||
var gameEvent = new GameEvent()
|
||||
{
|
||||
Target = target,
|
||||
Origin = target
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(gameEvent);
|
||||
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Tell));
|
||||
Assert.IsEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Command));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_RunAsFailsOnHigherPrivilege()
|
||||
{
|
||||
var cmd = new RunAsCommand(cmdConfig, transLookup);
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
var target = ClientGenerators.CreateBasicClient(server);
|
||||
target.Level = EFClient.Permission.Administrator;
|
||||
var origin = ClientGenerators.CreateBasicClient(server);
|
||||
origin.NetworkId = 100;
|
||||
origin.Level = EFClient.Permission.Moderator;
|
||||
|
||||
var gameEvent = new GameEvent()
|
||||
{
|
||||
Target = target,
|
||||
Origin = origin
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(gameEvent);
|
||||
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Tell));
|
||||
Assert.IsEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Command));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_RunAsFailsOnSamePrivilege()
|
||||
{
|
||||
var cmd = new RunAsCommand(cmdConfig, transLookup);
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
var target = ClientGenerators.CreateBasicClient(server);
|
||||
target.Level = EFClient.Permission.Administrator;
|
||||
var origin = ClientGenerators.CreateBasicClient(server);
|
||||
origin.NetworkId = 100;
|
||||
origin.Level = EFClient.Permission.Administrator;
|
||||
|
||||
var gameEvent = new GameEvent()
|
||||
{
|
||||
Target = target,
|
||||
Origin = origin
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(gameEvent);
|
||||
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Tell));
|
||||
Assert.IsEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Command));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_RunAsFailsOnDisallowedCommand()
|
||||
{
|
||||
var cmd = new RunAsCommand(cmdConfig, transLookup);
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
var target = ClientGenerators.CreateBasicClient(server);
|
||||
target.Level = EFClient.Permission.Moderator;
|
||||
var origin = ClientGenerators.CreateBasicClient(server);
|
||||
origin.NetworkId = 100;
|
||||
origin.Level = EFClient.Permission.Administrator;
|
||||
|
||||
var gameEvent = new GameEvent()
|
||||
{
|
||||
Target = target,
|
||||
Origin = origin,
|
||||
Owner = server,
|
||||
Data = nameof(NonImpersonatableCommand)
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(gameEvent);
|
||||
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Tell));
|
||||
// failed when validating the command
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Command && _event.FailReason == GameEvent.EventFailReason.Invalid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test_RunAsQueuesEventAndResponse()
|
||||
{
|
||||
var cmd = new RunAsCommand(cmdConfig, transLookup);
|
||||
var server = serviceProvider.GetRequiredService<IW4MServer>();
|
||||
var target = ClientGenerators.CreateBasicClient(server);
|
||||
target.Level = EFClient.Permission.Moderator;
|
||||
var origin = ClientGenerators.CreateBasicClient(server);
|
||||
origin.NetworkId = 100;
|
||||
origin.Level = EFClient.Permission.Administrator;
|
||||
|
||||
var gameEvent = new GameEvent()
|
||||
{
|
||||
Target = target,
|
||||
Origin = origin,
|
||||
Data = nameof(ImpersonatableCommand),
|
||||
Owner = server
|
||||
};
|
||||
|
||||
await cmd.ExecuteAsync(gameEvent);
|
||||
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Tell /*&& _event.Target == origin todo: fake the command result*/ ));
|
||||
Assert.IsNotEmpty(mockEventHandler.Events.Where(_event => _event.Type == GameEvent.EventType.Command && !_event.Failed));
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
36
Tests/ApplicationTests/Mocks/Commands.cs
Normal file
36
Tests/ApplicationTests/Mocks/Commands.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ApplicationTests.Mocks
|
||||
{
|
||||
class ImpersonatableCommand : Command
|
||||
{
|
||||
public ImpersonatableCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
|
||||
{
|
||||
AllowImpersonation = true;
|
||||
Name = nameof(ImpersonatableCommand);
|
||||
}
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
E.Origin.Tell("test");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
class NonImpersonatableCommand : Command
|
||||
{
|
||||
public NonImpersonatableCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
|
||||
{
|
||||
Name = nameof(NonImpersonatableCommand);
|
||||
}
|
||||
|
||||
public override Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,10 +7,22 @@ namespace ApplicationTests.Mocks
|
||||
class MockEventHandler : IEventHandler
|
||||
{
|
||||
public IList<GameEvent> Events = new List<GameEvent>();
|
||||
private readonly bool _autoExecute;
|
||||
|
||||
public MockEventHandler(bool autoExecute = false)
|
||||
{
|
||||
_autoExecute = autoExecute;
|
||||
}
|
||||
|
||||
public void AddEvent(GameEvent gameEvent)
|
||||
{
|
||||
Events.Add(gameEvent);
|
||||
|
||||
if (_autoExecute)
|
||||
{
|
||||
gameEvent.Owner?.ExecuteEvent(gameEvent);
|
||||
gameEvent.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
45
WebfrontCore/Controllers/AdminController.cs
Normal file
45
WebfrontCore/Controllers/AdminController.cs
Normal file
@ -0,0 +1,45 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharedLibraryCore;
|
||||
using SharedLibraryCore.Dtos;
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebfrontCore.Controllers
|
||||
{
|
||||
public class AdminController : BaseController
|
||||
{
|
||||
private readonly IAuditInformationRepository _auditInformationRepository;
|
||||
private readonly ITranslationLookup _translationLookup;
|
||||
private static readonly int DEFAULT_COUNT = 25;
|
||||
|
||||
public AdminController(IManager manager, IAuditInformationRepository auditInformationRepository, ITranslationLookup translationLookup) : base(manager)
|
||||
{
|
||||
_auditInformationRepository = auditInformationRepository;
|
||||
_translationLookup = translationLookup;
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IActionResult> AuditLog()
|
||||
{
|
||||
ViewBag.EnableColorCodes = Manager.GetApplicationSettings().Configuration().EnableColorCodes;
|
||||
ViewBag.IsFluid = true;
|
||||
ViewBag.Title = _translationLookup["WEBFRONT_NAV_AUDIT_LOG"];
|
||||
ViewBag.InitialOffset = DEFAULT_COUNT;
|
||||
|
||||
var auditItems = await _auditInformationRepository.ListAuditInformation(new PaginationInfo()
|
||||
{
|
||||
Count = DEFAULT_COUNT
|
||||
});
|
||||
|
||||
return View(auditItems);
|
||||
}
|
||||
|
||||
public async Task<IActionResult> ListAuditLog([FromQuery] PaginationInfo paginationInfo)
|
||||
{
|
||||
ViewBag.EnableColorCodes = Manager.GetApplicationSettings().Configuration().EnableColorCodes;
|
||||
var auditItems = await _auditInformationRepository.ListAuditInformation(paginationInfo);
|
||||
return PartialView("_ListAuditLog", auditItems);
|
||||
}
|
||||
}
|
||||
}
|
@ -101,7 +101,12 @@ namespace WebfrontCore
|
||||
#endif
|
||||
|
||||
services.AddSingleton(Program.Manager);
|
||||
|
||||
// todo: this needs to be handled more gracefully
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IConfigurationHandlerFactory>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IDatabaseContextFactory>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<IAuditInformationRepository>());
|
||||
services.AddSingleton(Program.ApplicationServiceProvider.GetService<ITranslationLookup>());
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
34
WebfrontCore/Views/Admin/AuditLog.cshtml
Normal file
34
WebfrontCore/Views/Admin/AuditLog.cshtml
Normal file
@ -0,0 +1,34 @@
|
||||
@{
|
||||
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
|
||||
}
|
||||
<h4 class="pb-3 text-center">@ViewBag.Title</h4>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead class="d-none d-lg-table-header-group">
|
||||
<tr class="bg-primary pt-2 pb-2">
|
||||
<th scope="col">@loc["WEBFRONT_PENALTY_TEMPLATE_TYPE"]</th>
|
||||
<th scope="col">@loc["WEBFRONT_PENALTY_TEMPLATE_ADMIN"]</th>
|
||||
<th scope="col">@loc["WEBFRONT_PENALTY_TEMPLATE_NAME"]</th>
|
||||
<th scope="col">@loc["WEBFRONT_ADMIN_AUDIT_LOG_INFO"]</th>
|
||||
<!--<th scope="col">@loc["WEBFRONT_ADMIN_AUDIT_LOG_PREVIOUS"]</th>-->
|
||||
<th scope="col">@loc["WEBFRONT_ADMIN_AUDIT_LOG_CURRENT"]</th>
|
||||
<th scope="col" class="text-right">@loc["WEBFRONT_ADMIN_AUDIT_LOG_TIME"]</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="audit_log_table_body" class="border-bottom bg-dark">
|
||||
<partial name="_ListAuditLog" />
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
<span id="load_audit_log_button" class="loader-load-more oi oi-chevron-bottom text-center text-primary w-100 h3 pb-0 mb-0 d-none d-lg-block"></span>
|
||||
|
||||
@section scripts {
|
||||
<environment include="Development">
|
||||
<script type="text/javascript" src="~/js/loader.js"></script>
|
||||
</environment>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
initLoader('/Admin/ListAuditLog', '#audit_log_table_body', @ViewBag.IntialOffset);
|
||||
});
|
||||
</script>
|
||||
}
|
99
WebfrontCore/Views/Admin/_ListAuditLog.cshtml
Normal file
99
WebfrontCore/Views/Admin/_ListAuditLog.cshtml
Normal file
@ -0,0 +1,99 @@
|
||||
@using SharedLibraryCore.Dtos
|
||||
@model IEnumerable<AuditInfo>
|
||||
@{
|
||||
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
|
||||
}
|
||||
|
||||
@foreach (var info in Model)
|
||||
{
|
||||
<!-- mobile -->
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_PENALTY_TEMPLATE_TYPE"]</th>
|
||||
<td class="text-light">
|
||||
@info.Action
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_PENALTY_TEMPLATE_ADMIN"]</th>
|
||||
<td>
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@info.OriginId" class="link-inverse">
|
||||
<color-code value="@info.OriginName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_PENALTY_TEMPLATE_NAME"]</th>
|
||||
<td>
|
||||
@if (info.TargetId != null)
|
||||
{
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@info.TargetId" class="link-inverse">
|
||||
<color-code value="@info.TargetName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>--</span>
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_ADMIN_AUDIT_LOG_INFO"]</th>
|
||||
<td class="text-light">
|
||||
@info.Data
|
||||
</td>
|
||||
</tr>
|
||||
@*<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_ADMIN_AUDIT_LOG_PREVIOUS"]</th>
|
||||
<td class="text-light">
|
||||
@info.OldValue
|
||||
</td>
|
||||
</tr>*@
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="bg-primary">@loc["WEBFRONT_ADMIN_AUDIT_LOG_CURRENT"]</th>
|
||||
<td class="text-light">
|
||||
@info.NewValue
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="d-table-row d-lg-none bg-dark">
|
||||
<th scope="row" class="w-25 bg-primary" style="border-bottom: 1px solid #222">@loc["WEBFRONT_ADMIN_AUDIT_LOG_TIME"]</th>
|
||||
<td class="text-light mb-2 border-bottom">
|
||||
@info.When.ToString()
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- desktop -->
|
||||
<tr class="d-none d-lg-table-row">
|
||||
<td class="text-light font-weight-bold">
|
||||
@info.Action
|
||||
</td>
|
||||
<td>
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@info.OriginId" class="link-inverse">
|
||||
<color-code value="@info.OriginName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
@if (info.TargetId != null)
|
||||
{
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@info.TargetId" class="link-inverse">
|
||||
<color-code value="@info.TargetName" allow="@ViewBag.EnableColorCodes"></color-code>
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span>--</span>
|
||||
}
|
||||
</td>
|
||||
<td class="text-light">
|
||||
@info.Data
|
||||
|
||||
@*<td class="text-light">
|
||||
@info.OldValue
|
||||
</td>*@
|
||||
<td class="text-light">
|
||||
@info.NewValue
|
||||
</td>
|
||||
<td class="text-light text-right">
|
||||
@info.When.ToString()
|
||||
</td>
|
||||
</tr>
|
||||
}
|
@ -39,38 +39,39 @@
|
||||
<li class="nav-item text-center text-lg-left">@Html.ActionLink(loc["WEBFRONT_NAV_HELP"], "Help", "Home", new { area = "" }, new { @class = "nav-link" })</li>
|
||||
@foreach (var _page in ViewBag.Pages)
|
||||
{
|
||||
<li class="nav-item text-center text-lg-left">
|
||||
<a class="nav-link" href="@_page.Location">@_page.Name</a>
|
||||
</li>
|
||||
<li class="nav-item text-center text-lg-left">
|
||||
<a class="nav-link" href="@_page.Location">@_page.Name</a>
|
||||
</li>
|
||||
}
|
||||
<li class="nav-item text-center text-lg-left"></li>
|
||||
@if (!string.IsNullOrEmpty(ViewBag.SocialLink))
|
||||
{
|
||||
<li class="nav-item text-center text-lg-left"><a href="@ViewBag.SocialLink" class="nav-link" target="_blank">@ViewBag.SocialTitle</a></li>
|
||||
<li class="nav-item text-center text-lg-left"><a href="@ViewBag.SocialLink" class="nav-link" target="_blank">@ViewBag.SocialTitle</a></li>
|
||||
}
|
||||
@if (ViewBag.Authorized)
|
||||
{
|
||||
<li class="nav-link dropdown text-center text-lg-left p-0">
|
||||
<a href="#" class="nav-link oi oi-person dropdown-toggle oi-fix-navbar w-100" id="account_dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></a>
|
||||
<li class="nav-link dropdown text-center text-lg-left p-0">
|
||||
<a href="#" class="nav-link oi oi-person dropdown-toggle oi-fix-navbar w-100" id="account_dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></a>
|
||||
|
||||
<div class="dropdown-menu p-0" aria-labelledby="account_dropdown">
|
||||
<a asp-controller="Console" asp-action="Index" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_CONSOLE"]</a>
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@ViewBag.User.ClientId" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_PROFILE"]</a>
|
||||
@if (ViewBag.User.Level >= SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
|
||||
<div class="dropdown-menu p-0" aria-labelledby="account_dropdown">
|
||||
<a asp-controller="Console" asp-action="Index" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_CONSOLE"]</a>
|
||||
<a asp-controller="Client" asp-action="ProfileAsync" asp-route-id="@ViewBag.User.ClientId" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_PROFILE"]</a>
|
||||
@if (ViewBag.User.Level >= SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
|
||||
{
|
||||
<a asp-controller="Configuration" asp-action="Edit" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_EDIT_CONFIGURATION"]</a>
|
||||
<a asp-controller="Configuration" asp-action="Edit" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_EDIT_CONFIGURATION"]</a>
|
||||
}
|
||||
<a class="dropdown-item bg-dark text-muted text-center text-lg-left profile-action" href="#" data-action="RecentClients" title="@loc["WEBFRONT_ACTION_RECENT_CLIENTS"]">@loc["WEBFRONT_ACTION_RECENT_CLIENTS"]</a>
|
||||
<a class="dropdown-item bg-dark text-muted text-center text-lg-left profile-action" href="#" data-action="GenerateLoginToken" title="@loc["WEBFRONT_ACTION_TOKEN"]">@loc["WEBFRONT_ACTION_TOKEN"]</a>
|
||||
<a asp-controller="Account" asp-action="LogoutAsync" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_LOGOUT"]</a>
|
||||
</div>
|
||||
</li>
|
||||
<a asp-controller="Admin" asp-action="AuditLog" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_AUDIT_LOG"]</a>
|
||||
<a class="dropdown-item bg-dark text-muted text-center text-lg-left profile-action" href="#" data-action="RecentClients" title="@loc["WEBFRONT_ACTION_RECENT_CLIENTS"]">@loc["WEBFRONT_ACTION_RECENT_CLIENTS"]</a>
|
||||
<a class="dropdown-item bg-dark text-muted text-center text-lg-left profile-action" href="#" data-action="GenerateLoginToken" title="@loc["WEBFRONT_ACTION_TOKEN"]">@loc["WEBFRONT_ACTION_TOKEN"]</a>
|
||||
<a asp-controller="Account" asp-action="LogoutAsync" class="dropdown-item bg-dark text-muted text-center text-lg-left">@loc["WEBFRONT_NAV_LOGOUT"]</a>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
else
|
||||
{
|
||||
<li class="nav-item text-center text-md-left">
|
||||
<a href="#" id="profile_action_login_btn" class="nav-link profile-action oi oi-key oi-fix-navbar w-100" title="Login" data-action="login" aria-hidden="true"></a>
|
||||
</li>
|
||||
<li class="nav-item text-center text-md-left">
|
||||
<a href="#" id="profile_action_login_btn" class="nav-link profile-action oi oi-key oi-fix-navbar w-100" title="Login" data-action="login" aria-hidden="true"></a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
<form class="form-inline text-primary pt-3 pb-3" method="get" action="/Client/FindAsync">
|
||||
|
Loading…
x
Reference in New Issue
Block a user