Only allow a single approved IP address

This commit is contained in:
jvyden 2022-06-11 18:43:30 -04:00
parent f169236613
commit eb21c7042f
No known key found for this signature in database
GPG key ID: 18BCF2BE0262B278
16 changed files with 124 additions and 141 deletions

View file

@ -7,6 +7,12 @@
"commands": [ "commands": [
"dotnet-ef" "dotnet-ef"
] ]
},
"dotnet-trace": {
"version": "6.0.328102",
"commands": [
"dotnet-trace"
]
} }
} }
} }

View file

@ -4,6 +4,9 @@
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
</envs>
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" /> <option name="RUNTIME_ARGUMENTS" value="" />

View file

@ -4,6 +4,9 @@
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
</envs>
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" /> <option name="RUNTIME_ARGUMENTS" value="" />

View file

@ -4,6 +4,9 @@
<option name="PROGRAM_PARAMETERS" value="" /> <option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" /> <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ProjectLighthouse" />
<option name="PASS_PARENT_ENVS" value="1" /> <option name="PASS_PARENT_ENVS" value="1" />
<envs>
<env name="ASPNETCORE_ENVIRONMENT" value="Development" />
</envs>
<option name="USE_EXTERNAL_CONSOLE" value="0" /> <option name="USE_EXTERNAL_CONSOLE" value="0" />
<option name="USE_MONO" value="0" /> <option name="USE_MONO" value="0" />
<option name="RUNTIME_ARGUMENTS" value="" /> <option name="RUNTIME_ARGUMENTS" value="" />

View file

@ -82,7 +82,7 @@ public class LoginController : ControllerBase
if (ServerConfiguration.Instance.Authentication.UseExternalAuth) if (ServerConfiguration.Instance.Authentication.UseExternalAuth)
{ {
if (this.database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).Select(a => a.IpAddress).Contains(ipAddress)) if (user.ApprovedIPAddress == ipAddress)
{ {
token.Approved = true; token.Approved = true;
} }

View file

@ -32,15 +32,8 @@ public class AutoApprovalController : ControllerBase
if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login"); if (authAttempt.GameToken.UserId != user.UserId) return this.Redirect("/login");
authAttempt.GameToken.Approved = true; authAttempt.GameToken.Approved = true;
user.ApprovedIPAddress = authAttempt.IPAddress;
UserApprovedIpAddress approvedIpAddress = new()
{
UserId = user.UserId,
User = user,
IpAddress = authAttempt.IPAddress,
};
this.database.UserApprovedIpAddresses.Add(approvedIpAddress);
this.database.AuthenticationAttempts.Remove(authAttempt); this.database.AuthenticationAttempts.Remove(authAttempt);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
@ -48,20 +41,16 @@ public class AutoApprovalController : ControllerBase
return this.Redirect("/authentication"); return this.Redirect("/authentication");
} }
[HttpGet("revokeAutoApproval/{id:int}")] [HttpGet("revokeAutoApproval")]
public async Task<IActionResult> RevokeAutoApproval([FromRoute] int id) public async Task<IActionResult> RevokeAutoApproval()
{ {
User? user = this.database.UserFromWebRequest(this.Request); User? user = this.database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("/login"); if (user == null) return this.Redirect("/login");
UserApprovedIpAddress? approvedIpAddress = await this.database.UserApprovedIpAddresses.FirstOrDefaultAsync(a => a.UserApprovedIpAddressId == id); user.ApprovedIPAddress = null;
if (approvedIpAddress == null) return this.BadRequest();
if (approvedIpAddress.UserId != user.UserId) return this.Redirect("/login");
this.database.UserApprovedIpAddresses.Remove(approvedIpAddress);
await this.database.SaveChangesAsync(); await this.database.SaveChangesAsync();
return this.Redirect("/authentication/autoApprovals"); return this.Redirect("/authentication");
} }
} }

View file

@ -21,18 +21,24 @@ else
} }
} }
<a href="/authentication/autoApprovals"> @if (Model.User!.ApprovedIPAddress != null)
<button class="ui small blue button"> {
<i class="cog icon"></i> <a href="/authentication/revokeAutoApproval">
<span>Manage automatically approved IP addresses</span> <button class="ui red button">
</button> <i class="trash icon"></i>
</a> <span>Revoke automatically approved IP Address (@Model.User!.ApprovedIPAddress)</span>
<a href="/authentication/denyAll"> </button>
<button class="ui small red button"> </a>
<i class="x icon"></i> }
<span>Deny all</span> @if (Model.AuthenticationAttempts.Count > 1)
</button> {
</a> <a href="/authentication/denyAll">
<button class="ui red button">
<i class="x icon"></i>
<span>Deny all</span>
</button>
</a>
}
@foreach (AuthenticationAttempt authAttempt in Model.AuthenticationAttempts) @foreach (AuthenticationAttempt authAttempt in Model.AuthenticationAttempts)
{ {
@ -41,19 +47,19 @@ else
<p>A <b>@authAttempt.Platform</b> authentication request was logged at <b>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</b> from the IP address <b>@authAttempt.IPAddress</b>.</p> <p>A <b>@authAttempt.Platform</b> authentication request was logged at <b>@timestamp.ToString("MM/dd/yyyy @ h:mm tt") UTC</b> from the IP address <b>@authAttempt.IPAddress</b>.</p>
<div> <div>
<a href="/authentication/autoApprove/@authAttempt.AuthenticationAttemptId"> <a href="/authentication/autoApprove/@authAttempt.AuthenticationAttemptId">
<button class="ui tiny green button"> <button class="ui small green button">
<i class="check icon"></i> <i class="check icon"></i>
<span>Automatically approve every time</span> <span>Automatically approve every time</span>
</button> </button>
</a> </a>
<a href="/authentication/approve/@authAttempt.AuthenticationAttemptId"> <a href="/authentication/approve/@authAttempt.AuthenticationAttemptId">
<button class="ui tiny yellow button"> <button class="ui small yellow button">
<i class="check icon"></i> <i class="check icon"></i>
<span>Approve this time</span> <span>Approve this time</span>
</button> </button>
</a> </a>
<a href="/authentication/deny/@authAttempt.AuthenticationAttemptId"> <a href="/authentication/deny/@authAttempt.AuthenticationAttemptId">
<button class="ui tiny red button"> <button class="ui small red button">
<i class="x icon"></i> <i class="x icon"></i>
<span>Deny</span> <span>Deny</span>
</button> </button>

View file

@ -1,23 +0,0 @@
@page "/authentication/autoApprovals"
@using LBPUnion.ProjectLighthouse.PlayerData.Profiles
@using LBPUnion.ProjectLighthouse.Types
@model LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth.ManageUserApprovedIpAddressesPage
@{
Layout = "Layouts/BaseLayout";
Model.Title = "Automatically approved IP addresses";
}
@foreach (UserApprovedIpAddress approvedIpAddress in Model.ApprovedIpAddresses)
{
<div class="ui blue segment">
<p>@approvedIpAddress.IpAddress</p>
<a href="/authentication/revokeAutoApproval/@approvedIpAddress.UserApprovedIpAddressId">
<button class="ui red button">
<i class="trash icon"></i>
<span>Revoke</span>
</button>
</a>
</div>
}

View file

@ -1,26 +0,0 @@
#nullable enable
using LBPUnion.ProjectLighthouse.PlayerData.Profiles;
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
using LBPUnion.ProjectLighthouse.Types;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
namespace LBPUnion.ProjectLighthouse.Servers.Website.Pages.ExternalAuth;
public class ManageUserApprovedIpAddressesPage : BaseLayout
{
public List<UserApprovedIpAddress> ApprovedIpAddresses = new();
public ManageUserApprovedIpAddressesPage(Database database) : base(database)
{}
public async Task<IActionResult> OnGet()
{
User? user = this.Database.UserFromWebRequest(this.Request);
if (user == null) return this.Redirect("/login");
this.ApprovedIpAddresses = await this.Database.UserApprovedIpAddresses.Where(a => a.UserId == user.UserId).ToListAsync();
return this.Page();
}
}

View file

@ -10,36 +10,36 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj"/> <ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Remove="gitVersion.txt"/> <None Remove="gitVersion.txt" />
<EmbeddedResource Include="gitVersion.txt"> <EmbeddedResource Include="gitVersion.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<None Remove="gitBranch.txt"/> <None Remove="gitBranch.txt" />
<EmbeddedResource Include="gitBranch.txt"> <EmbeddedResource Include="gitBranch.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<None Remove="gitRemotes.txt"/> <None Remove="gitRemotes.txt" />
<EmbeddedResource Include="gitRemotes.txt"> <EmbeddedResource Include="gitRemotes.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
<None Remove="gitUnpushed.txt"/> <None Remove="gitUnpushed.txt" />
<EmbeddedResource Include="gitUnpushed.txt"> <EmbeddedResource Include="gitUnpushed.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.5"/> <PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.5" />
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;"/> <Exec Command="git describe --long --always --dirty --exclude=\* --abbrev=8 &gt; &quot;$(ProjectDir)/gitVersion.txt&quot;" />
<Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;"/> <Exec Command="git branch --show-current &gt; &quot;$(ProjectDir)/gitBranch.txt&quot;" />
<Exec Command="git remote -v &gt; &quot;$(ProjectDir)/gitRemotes.txt&quot;"/> <Exec Command="git remote -v &gt; &quot;$(ProjectDir)/gitRemotes.txt&quot;" />
<Exec Command="git log --branches --not --remotes --oneline &gt; &quot;$(ProjectDir)/gitUnpushed.txt&quot;"/> <Exec Command="git log --branches --not --remotes --oneline &gt; &quot;$(ProjectDir)/gitUnpushed.txt&quot;" />
</Target> </Target>
</Project> </Project>

View file

@ -58,7 +58,10 @@ public class WebsiteStartup
app.UseRouting(); app.UseRouting();
app.UseStaticFiles(); app.UseStaticFiles(new StaticFileOptions
{
ServeUnknownFileTypes = true,
});
app.UseEndpoints(endpoints => endpoints.MapControllers()); app.UseEndpoints(endpoints => endpoints.MapControllers());
app.UseEndpoints(endpoints => endpoints.MapRazorPages()); app.UseEndpoints(endpoints => endpoints.MapRazorPages());

View file

@ -41,7 +41,6 @@ public class Database : DbContext
public DbSet<AuthenticationAttempt> AuthenticationAttempts { get; set; } public DbSet<AuthenticationAttempt> AuthenticationAttempts { get; set; }
public DbSet<Review> Reviews { get; set; } public DbSet<Review> Reviews { get; set; }
public DbSet<RatedReview> RatedReviews { get; set; } public DbSet<RatedReview> RatedReviews { get; set; }
public DbSet<UserApprovedIpAddress> UserApprovedIpAddresses { get; set; }
public DbSet<DatabaseCategory> CustomCategories { get; set; } public DbSet<DatabaseCategory> CustomCategories { get; set; }
public DbSet<Reaction> Reactions { get; set; } public DbSet<Reaction> Reactions { get; set; }
public DbSet<GriefReport> Reports { get; set; } public DbSet<GriefReport> Reports { get; set; }

View file

@ -0,0 +1,61 @@
using LBPUnion.ProjectLighthouse;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ProjectLighthouse.Migrations
{
[DbContext(typeof(Database))]
[Migration("20220611221819_OnlyAllowSingleApprovedIP")]
public class OnlyAllowSingleApprovedIP : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "UserApprovedIpAddresses");
migrationBuilder.AddColumn<string>(
name: "ApprovedIPAddress",
table: "Users",
type: "longtext",
nullable: true)
.Annotation("MySql:CharSet", "utf8mb4");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "ApprovedIPAddress",
table: "Users");
migrationBuilder.CreateTable(
name: "UserApprovedIpAddresses",
columns: table => new
{
UserApprovedIpAddressId = table.Column<int>(type: "int", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
UserId = table.Column<int>(type: "int", nullable: false),
IpAddress = table.Column<string>(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4")
},
constraints: table =>
{
table.PrimaryKey("PK_UserApprovedIpAddresses", x => x.UserApprovedIpAddressId);
table.ForeignKey(
name: "FK_UserApprovedIpAddresses_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "UserId",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySql:CharSet", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_UserApprovedIpAddresses_UserId",
table: "UserApprovedIpAddresses",
column: "UserId");
}
}
}

View file

@ -595,6 +595,9 @@ namespace ProjectLighthouse.Migrations
b.Property<int>("AdminGrantedSlots") b.Property<int>("AdminGrantedSlots")
.HasColumnType("int"); .HasColumnType("int");
b.Property<string>("ApprovedIPAddress")
.HasColumnType("longtext");
b.Property<bool>("Banned") b.Property<bool>("Banned")
.HasColumnType("tinyint(1)"); .HasColumnType("tinyint(1)");
@ -659,25 +662,6 @@ namespace ProjectLighthouse.Migrations
b.ToTable("Users"); b.ToTable("Users");
}); });
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b =>
{
b.Property<int>("UserApprovedIpAddressId")
.ValueGeneratedOnAdd()
.HasColumnType("int");
b.Property<string>("IpAddress")
.HasColumnType("longtext");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("UserApprovedIpAddressId");
b.HasIndex("UserId");
b.ToTable("UserApprovedIpAddresses");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reaction", b => modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reaction", b =>
{ {
b.Property<int>("RatingId") b.Property<int>("RatingId")
@ -1035,17 +1019,6 @@ namespace ProjectLighthouse.Migrations
b.Navigation("Location"); b.Navigation("Location");
}); });
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Profiles.UserApprovedIpAddress", b =>
{
b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
});
modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b => modelBuilder.Entity("LBPUnion.ProjectLighthouse.PlayerData.Reviews.RatedReview", b =>
{ {
b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", "Review") b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Reviews.Review", "Review")

View file

@ -142,6 +142,9 @@ public class User
[JsonIgnore] [JsonIgnore]
public string BannedReason { get; set; } public string BannedReason { get; set; }
[JsonIgnore]
public string? ApprovedIPAddress { get; set; }
public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1) public string Serialize(GameVersion gameVersion = GameVersion.LittleBigPlanet1)
{ {

View file

@ -1,17 +0,0 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace LBPUnion.ProjectLighthouse.PlayerData.Profiles;
public class UserApprovedIpAddress
{
[Key]
public int UserApprovedIpAddressId { get; set; }
public int UserId { get; set; }
[ForeignKey(nameof(UserId))]
public User User { get; set; }
public string IpAddress { get; set; }
}