feat(backend): make field limits configurable
This commit is contained in:
parent
7ea6c62d67
commit
218c756a70
7 changed files with 133 additions and 149 deletions
|
@ -99,6 +99,11 @@ public class Config
|
|||
{
|
||||
public int MaxMemberCount { get; init; } = 1000;
|
||||
|
||||
public int MaxFields { get; init; } = 25;
|
||||
public int MaxFieldNameLength { get; init; } = 100;
|
||||
public int MaxFieldEntryTextLength { get; init; } = 100;
|
||||
public int MaxFieldEntries { get; init; } = 100;
|
||||
|
||||
public int MaxUsernameLength { get; init; } = 40;
|
||||
public int MaxMemberNameLength { get; init; } = 100;
|
||||
public int MaxDisplayNameLength { get; init; } = 100;
|
||||
|
|
|
@ -81,13 +81,13 @@ public class MembersController(
|
|||
("display_name", validationService.ValidateDisplayName(req.DisplayName)),
|
||||
("bio", validationService.ValidateBio(req.Bio)),
|
||||
("avatar", validationService.ValidateAvatar(req.Avatar)),
|
||||
.. ValidationUtils.ValidateFields(req.Fields, CurrentUser!.CustomPreferences),
|
||||
.. ValidationUtils.ValidateFieldEntries(
|
||||
.. validationService.ValidateFields(req.Fields, CurrentUser!.CustomPreferences),
|
||||
.. validationService.ValidateFieldEntries(
|
||||
req.Names?.ToArray(),
|
||||
CurrentUser!.CustomPreferences,
|
||||
"names"
|
||||
),
|
||||
.. ValidationUtils.ValidatePronouns(
|
||||
.. validationService.ValidatePronouns(
|
||||
req.Pronouns?.ToArray(),
|
||||
CurrentUser!.CustomPreferences
|
||||
),
|
||||
|
@ -191,7 +191,7 @@ public class MembersController(
|
|||
if (req.Names != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidateFieldEntries(
|
||||
validationService.ValidateFieldEntries(
|
||||
req.Names,
|
||||
CurrentUser!.CustomPreferences,
|
||||
"names"
|
||||
|
@ -203,7 +203,7 @@ public class MembersController(
|
|||
if (req.Pronouns != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidatePronouns(req.Pronouns, CurrentUser!.CustomPreferences)
|
||||
validationService.ValidatePronouns(req.Pronouns, CurrentUser!.CustomPreferences)
|
||||
);
|
||||
member.Pronouns = req.Pronouns.ToList();
|
||||
}
|
||||
|
@ -211,7 +211,10 @@ public class MembersController(
|
|||
if (req.Fields != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidateFields(req.Fields.ToList(), CurrentUser!.CustomPreferences)
|
||||
validationService.ValidateFields(
|
||||
req.Fields.ToList(),
|
||||
CurrentUser!.CustomPreferences
|
||||
)
|
||||
);
|
||||
member.Fields = req.Fields.ToList();
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ public class UsersController(
|
|||
if (req.Names != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidateFieldEntries(
|
||||
validationService.ValidateFieldEntries(
|
||||
req.Names,
|
||||
CurrentUser!.CustomPreferences,
|
||||
"names"
|
||||
|
@ -103,7 +103,7 @@ public class UsersController(
|
|||
if (req.Pronouns != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidatePronouns(req.Pronouns, CurrentUser!.CustomPreferences)
|
||||
validationService.ValidatePronouns(req.Pronouns, CurrentUser!.CustomPreferences)
|
||||
);
|
||||
user.Pronouns = req.Pronouns.ToList();
|
||||
}
|
||||
|
@ -111,7 +111,10 @@ public class UsersController(
|
|||
if (req.Fields != null)
|
||||
{
|
||||
errors.AddRange(
|
||||
ValidationUtils.ValidateFields(req.Fields.ToList(), CurrentUser!.CustomPreferences)
|
||||
validationService.ValidateFields(
|
||||
req.Fields.ToList(),
|
||||
CurrentUser!.CustomPreferences
|
||||
)
|
||||
);
|
||||
user.Fields = req.Fields.ToList();
|
||||
}
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
|
||||
namespace Foxnouns.Backend.Utils;
|
||||
namespace Foxnouns.Backend.Services;
|
||||
|
||||
public static partial class ValidationUtils
|
||||
public partial class ValidationService
|
||||
{
|
||||
public static readonly string[] DefaultStatusOptions =
|
||||
[
|
||||
|
@ -28,7 +28,7 @@ public static partial class ValidationUtils
|
|||
"avoid",
|
||||
];
|
||||
|
||||
public static IEnumerable<(string, ValidationError?)> ValidateFields(
|
||||
public IEnumerable<(string, ValidationError?)> ValidateFields(
|
||||
List<Field>? fields,
|
||||
IReadOnlyDictionary<Snowflake, User.CustomPreference> customPreferences
|
||||
)
|
||||
|
@ -37,7 +37,7 @@ public static partial class ValidationUtils
|
|||
return [];
|
||||
|
||||
var errors = new List<(string, ValidationError?)>();
|
||||
if (fields.Count > 25)
|
||||
if (fields.Count > _limits.MaxFields)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
|
@ -45,7 +45,7 @@ public static partial class ValidationUtils
|
|||
ValidationError.LengthError(
|
||||
"Too many fields",
|
||||
0,
|
||||
Limits.FieldLimit,
|
||||
_limits.MaxFields,
|
||||
fields.Count
|
||||
)
|
||||
)
|
||||
|
@ -53,39 +53,38 @@ public static partial class ValidationUtils
|
|||
}
|
||||
|
||||
// No overwhelming this function, thank you
|
||||
if (fields.Count > 100)
|
||||
if (fields.Count > _limits.MaxFields + 50)
|
||||
return errors;
|
||||
|
||||
foreach ((Field? field, int index) in fields.Select((field, index) => (field, index)))
|
||||
{
|
||||
switch (field.Name.Length)
|
||||
if (field.Name.Length > _limits.MaxFieldNameLength)
|
||||
{
|
||||
case > Limits.FieldNameLimit:
|
||||
errors.Add(
|
||||
(
|
||||
$"fields.{index}.name",
|
||||
ValidationError.LengthError(
|
||||
"Field name is too long",
|
||||
1,
|
||||
Limits.FieldNameLimit,
|
||||
field.Name.Length
|
||||
)
|
||||
errors.Add(
|
||||
(
|
||||
$"fields.{index}.name",
|
||||
ValidationError.LengthError(
|
||||
"Field name is too long",
|
||||
1,
|
||||
_limits.MaxFieldNameLength,
|
||||
field.Name.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
case < 1:
|
||||
errors.Add(
|
||||
(
|
||||
$"fields.{index}.name",
|
||||
ValidationError.LengthError(
|
||||
"Field name is too short",
|
||||
1,
|
||||
Limits.FieldNameLimit,
|
||||
field.Name.Length
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (field.Name.Length < 1)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
$"fields.{index}.name",
|
||||
ValidationError.LengthError(
|
||||
"Field name is too short",
|
||||
1,
|
||||
_limits.MaxFieldNameLength,
|
||||
field.Name.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
errors = errors
|
||||
|
@ -102,7 +101,7 @@ public static partial class ValidationUtils
|
|||
return errors;
|
||||
}
|
||||
|
||||
public static IEnumerable<(string, ValidationError?)> ValidateFieldEntries(
|
||||
public IEnumerable<(string, ValidationError?)> ValidateFieldEntries(
|
||||
FieldEntry[]? entries,
|
||||
IReadOnlyDictionary<Snowflake, User.CustomPreference> customPreferences,
|
||||
string errorPrefix = "fields"
|
||||
|
@ -112,7 +111,7 @@ public static partial class ValidationUtils
|
|||
return [];
|
||||
var errors = new List<(string, ValidationError?)>();
|
||||
|
||||
if (entries.Length > Limits.FieldEntriesLimit)
|
||||
if (entries.Length > _limits.MaxFieldEntries)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
|
@ -120,7 +119,7 @@ public static partial class ValidationUtils
|
|||
ValidationError.LengthError(
|
||||
"Field has too many entries",
|
||||
0,
|
||||
Limits.FieldEntriesLimit,
|
||||
_limits.MaxFieldEntries,
|
||||
entries.Length
|
||||
)
|
||||
)
|
||||
|
@ -128,7 +127,7 @@ public static partial class ValidationUtils
|
|||
}
|
||||
|
||||
// Same as above, no overwhelming this function with a ridiculous amount of entries
|
||||
if (entries.Length > Limits.FieldEntriesLimit + 50)
|
||||
if (entries.Length > _limits.MaxFieldEntries + 50)
|
||||
return errors;
|
||||
|
||||
string[] customPreferenceIds = customPreferences.Keys.Select(id => id.ToString()).ToArray();
|
||||
|
@ -139,34 +138,33 @@ public static partial class ValidationUtils
|
|||
)
|
||||
)
|
||||
{
|
||||
switch (entry.Value.Length)
|
||||
if (entry.Value.Length > _limits.MaxFieldEntryTextLength)
|
||||
{
|
||||
case > Limits.FieldEntryTextLimit:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Field value is too long",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Field value is too long",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
case < 1:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Field value is too short",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (entry.Value.Length < 1)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Field value is too short",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
|
@ -186,7 +184,7 @@ public static partial class ValidationUtils
|
|||
return errors;
|
||||
}
|
||||
|
||||
public static IEnumerable<(string, ValidationError?)> ValidatePronouns(
|
||||
public IEnumerable<(string, ValidationError?)> ValidatePronouns(
|
||||
Pronoun[]? entries,
|
||||
IReadOnlyDictionary<Snowflake, User.CustomPreference> customPreferences,
|
||||
string errorPrefix = "pronouns"
|
||||
|
@ -196,7 +194,7 @@ public static partial class ValidationUtils
|
|||
return [];
|
||||
var errors = new List<(string, ValidationError?)>();
|
||||
|
||||
if (entries.Length > Limits.FieldEntriesLimit)
|
||||
if (entries.Length > _limits.MaxFieldEntries)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
|
@ -204,7 +202,7 @@ public static partial class ValidationUtils
|
|||
ValidationError.LengthError(
|
||||
"Too many pronouns",
|
||||
0,
|
||||
Limits.FieldEntriesLimit,
|
||||
_limits.MaxFieldEntries,
|
||||
entries.Length
|
||||
)
|
||||
)
|
||||
|
@ -212,7 +210,7 @@ public static partial class ValidationUtils
|
|||
}
|
||||
|
||||
// Same as above, no overwhelming this function with a ridiculous amount of entries
|
||||
if (entries.Length > Limits.FieldEntriesLimit + 50)
|
||||
if (entries.Length > _limits.MaxFieldEntries + 50)
|
||||
return errors;
|
||||
|
||||
string[] customPreferenceIds = customPreferences.Keys.Select(id => id.ToString()).ToArray();
|
||||
|
@ -221,66 +219,64 @@ public static partial class ValidationUtils
|
|||
(Pronoun? entry, int entryIdx) in entries.Select((entry, entryIdx) => (entry, entryIdx))
|
||||
)
|
||||
{
|
||||
switch (entry.Value.Length)
|
||||
if (entry.Value.Length > _limits.MaxFieldEntryTextLength)
|
||||
{
|
||||
case > Limits.FieldEntryTextLimit:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun value is too long",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun value is too long",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
case < 1:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun value is too short",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (entry.Value.Length < 1)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.value",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun value is too short",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (entry.DisplayText != null)
|
||||
{
|
||||
switch (entry.DisplayText.Length)
|
||||
if (entry.DisplayText.Length > _limits.MaxFieldEntryTextLength)
|
||||
{
|
||||
case > Limits.FieldEntryTextLimit:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.display_text",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun display text is too long",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.display_text",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun display text is too long",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
case < 1:
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.display_text",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun display text is too short",
|
||||
1,
|
||||
Limits.FieldEntryTextLimit,
|
||||
entry.Value.Length
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (entry.DisplayText.Length < 1)
|
||||
{
|
||||
errors.Add(
|
||||
(
|
||||
$"{errorPrefix}.{entryIdx}.display_text",
|
||||
ValidationError.LengthError(
|
||||
"Pronoun display text is too short",
|
||||
1,
|
||||
_limits.MaxFieldEntryTextLength,
|
||||
entry.Value.Length
|
||||
)
|
||||
);
|
||||
break;
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (C) 2023-present sam/u1f320 (vulpine.solutions)
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published
|
||||
// by the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
namespace Foxnouns.Backend.Utils;
|
||||
|
||||
public static class Limits
|
||||
{
|
||||
public const int FieldLimit = 25;
|
||||
public const int FieldNameLimit = 100;
|
||||
public const int FieldEntryTextLimit = 100;
|
||||
public const int FieldEntriesLimit = 100;
|
||||
}
|
|
@ -20,7 +20,7 @@ public static partial class ValidationUtils
|
|||
|
||||
public static ValidationError? ValidateReportContext(string? context) =>
|
||||
context?.Length > MaximumReportContextLength
|
||||
? ValidationError.GenericValidationError("Avatar is too large", null)
|
||||
? ValidationError.GenericValidationError("Report context is too long", null)
|
||||
: null;
|
||||
|
||||
public const int MinimumPasswordLength = 12;
|
||||
|
|
|
@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||
using Dapper;
|
||||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Foxnouns.Backend.Utils;
|
||||
using Foxnouns.Backend.Services;
|
||||
using Foxnouns.DataMigrator.Models;
|
||||
using NodaTime.Extensions;
|
||||
using Npgsql;
|
||||
|
@ -260,6 +260,6 @@ public class UserMigrator(
|
|||
{
|
||||
if (_preferenceIds.TryGetValue(id, out Snowflake preferenceId))
|
||||
return preferenceId.ToString();
|
||||
return ValidationUtils.DefaultStatusOptions.Contains(id) ? id : "okay";
|
||||
return ValidationService.DefaultStatusOptions.Contains(id) ? id : "okay";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue