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
		Add a link
		
	
		Reference in a new issue