Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions API/Controller/Tokens/ReportTokens.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Drawing;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OpenShock.API.Models.Requests;
using OpenShock.Common.Authentication;
using OpenShock.Common.Errors;
using OpenShock.Common.Problems;
using OpenShock.Common.Services.Turnstile;
using OpenShock.Common.Utils;
using System.Net;
using OpenShock.Common.Services.Webhook;

namespace OpenShock.API.Controller.Tokens;

public sealed partial class TokensController
{
/// <summary>
/// Endpoint to delete potentially compromised api tokens
/// </summary>
/// <param name="body"></param>
/// <param name="turnstileService"></param>
/// <param name="webhookService"></param>
/// <param name="cancellationToken"></param>
/// <response code="200">The tokens were deleted if found</response>
[HttpPost("report")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> ReportTokens(
[FromBody] ReportTokensRequest body,
[FromServices] ICloudflareTurnstileService turnstileService,
[FromServices] IWebhookService webhookService,
CancellationToken cancellationToken)
{
var remoteIP = HttpContext.GetRemoteIP();

var turnStile = await turnstileService.VerifyUserResponseToken(body.TurnstileResponse, remoteIP, cancellationToken);
if (!turnStile.IsT0)
{
var cfErrors = turnStile.AsT1.Value!;
if (cfErrors.All(err => err == CloduflareTurnstileError.InvalidResponse))
return Problem(TurnstileError.InvalidTurnstile);

return Problem(new OpenShockProblem("InternalServerError", "Internal Server Error", HttpStatusCode.InternalServerError));
}

_db.ApiTokenReports.Add(new Common.OpenShockDb.ApiTokenReport {
Id = Guid.CreateVersion7(),
ReportedAt = DateTime.UtcNow,
ReportedByUserId = CurrentUser.Id,
ReportedByIp = remoteIP,
ReportedByIpCountry = HttpContext.GetCFIPCountry(),
ReportedByUser = CurrentUser
});
await _db.SaveChangesAsync(cancellationToken);

var hashes = body.Secrets.Select(HashingUtils.HashSha256).ToArray();
await _db.ApiTokens.Where(x => hashes.Contains(x.TokenHash)).ExecuteDeleteAsync(cancellationToken);

await webhookService.SendWebhook("TokensReported",
$"Someone reported {body.Secrets.Length} secret(s) as leaked", "AAA", Color.OrangeRed);

return Ok();
}
}
23 changes: 1 addition & 22 deletions API/Controller/Tokens/Tokens.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using System.ComponentModel.DataAnnotations;
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OpenShock.API.Models.Requests;
using OpenShock.API.Models.Response;
using OpenShock.Common.Constants;
using OpenShock.Common.Errors;
using OpenShock.Common.Models;
using OpenShock.Common.OpenShockDb;
using OpenShock.Common.Problems;
using OpenShock.Common.Utils;
Expand Down Expand Up @@ -116,24 +115,4 @@ public async Task<IActionResult> EditToken([FromRoute] Guid tokenId, [FromBody]

return Ok();
}

public class EditTokenRequest
{
[StringLength(HardLimits.ApiKeyNameMaxLength, MinimumLength = 1, ErrorMessage = "API token length must be between {1} and {2}")]
public required string Name { get; set; }

[MaxLength(HardLimits.ApiKeyMaxPermissions, ErrorMessage = "API token permissions must be between {1} and {2}")]
public List<PermissionType> Permissions { get; set; } = [PermissionType.Shockers_Use];
}

public sealed class CreateTokenRequest : EditTokenRequest
{
public DateTime? ValidUntil { get; set; } = null;
}

public sealed class TokenCreatedResponse
{
public required string Token { get; set; }
public required Guid Id { get; set; }
}
}
6 changes: 6 additions & 0 deletions API/Models/Requests/CreateTokenRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace OpenShock.API.Models.Requests;

public sealed class CreateTokenRequest : EditTokenRequest
{
public DateTime? ValidUntil { get; set; } = null;
}
14 changes: 14 additions & 0 deletions API/Models/Requests/EditTokenRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System.ComponentModel.DataAnnotations;
using OpenShock.Common.Constants;
using OpenShock.Common.Models;

namespace OpenShock.API.Models.Requests;

public class EditTokenRequest
{
[StringLength(HardLimits.ApiKeyNameMaxLength, MinimumLength = 1, ErrorMessage = "API token length must be between {1} and {2}")]
public required string Name { get; set; }

[MaxLength(HardLimits.ApiKeyMaxPermissions, ErrorMessage = "API token permissions must be between {1} and {2}")]
public List<PermissionType> Permissions { get; set; } = [PermissionType.Shockers_Use];
}
10 changes: 10 additions & 0 deletions API/Models/Requests/ReportTokensRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.ComponentModel.DataAnnotations;

namespace OpenShock.API.Models.Requests;

public class ReportTokensRequest
{
[Required(AllowEmptyStrings = false)]
public required string TurnstileResponse { get; set; }
public required string[] Secrets { get; set; }
}
7 changes: 7 additions & 0 deletions API/Models/Response/TokenCreatedResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace OpenShock.API.Models.Response;

public sealed class TokenCreatedResponse
{
public required string Token { get; set; }
public required Guid Id { get; set; }
}
Loading
Loading