-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enhance serialization and contracts #12
Changes from all commits
2bd12d8
684548f
32a1ce6
4049e9f
3dc4f45
d48cef8
8078d0b
bce9f4b
f649688
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,50 @@ | ||
namespace Argon.Api.Grains.Persistence.States; | ||
|
||
using System.Runtime.Serialization; | ||
using MemoryPack; | ||
using MessagePack; | ||
|
||
[GenerateSerializer] | ||
[Serializable] | ||
[MemoryPackable] | ||
[Alias(nameof(UserStorage))] | ||
public sealed partial record UserStorage | ||
{ | ||
[Id(0)] public Guid Id { get; set; } = Guid.Empty; | ||
[Id(1)] public string Username { get; set; } = string.Empty; | ||
[Id(2)] public string Password { get; set; } = string.Empty; | ||
[Id(5)] public string AvatarUrl { get; set; } = string.Empty; | ||
[Id(3)] public DateTime CreatedAt { get; } = DateTime.UtcNow; | ||
[Id(4)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; | ||
[property: DataMember(Order = 0)] | ||
[property: MemoryPackOrder(0)] | ||
[property: Key(0)] | ||
[Id(0)] | ||
public Guid Id { get; set; } = Guid.Empty; | ||
|
||
[property: DataMember(Order = 1)] | ||
[property: MemoryPackOrder(1)] | ||
[property: Key(1)] | ||
[Id(1)] | ||
public string Username { get; set; } = string.Empty; | ||
|
||
[property: DataMember(Order = 2)] | ||
[property: MemoryPackOrder(2)] | ||
[property: Key(2)] | ||
[Id(2)] | ||
public string Password { get; set; } = string.Empty; | ||
Comment on lines
+25
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Security concern: Password field is being serialized. The Password field is marked for serialization across multiple frameworks. This could lead to security vulnerabilities if the serialized data is logged, cached, or transmitted. Consider one of these approaches:
- [property: DataMember(Order = 2)]
- [property: MemoryPackOrder(2)]
- [property: Key(2)]
[Id(2)]
+ [IgnoreDataMember]
+ [MemoryPackIgnore]
public string Password { get; set; } = string.Empty;
|
||
|
||
[property: DataMember(Order = 5)] | ||
[property: MemoryPackOrder(5)] | ||
[property: Key(5)] | ||
[Id(5)] | ||
public string AvatarUrl { get; set; } = string.Empty; | ||
Comment on lines
+31
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix inconsistent property ordering. The serialization order numbers are not sequential:
This inconsistency could lead to issues with serialization/deserialization compatibility. Recommend reordering to be sequential: - [property: DataMember(Order = 5)]
- [property: MemoryPackOrder(5)]
- [property: Key(5)]
+ [property: DataMember(Order = 3)]
+ [property: MemoryPackOrder(3)]
+ [property: Key(3)]
public string AvatarUrl { get; set; } = string.Empty; Also applies to: 19-23, 37-41, 43-47 |
||
|
||
[property: DataMember(Order = 3)] | ||
[property: MemoryPackOrder(3)] | ||
[property: Key(3)] | ||
[Id(3)] | ||
public DateTime CreatedAt { get; } = DateTime.UtcNow; | ||
|
||
[property: DataMember(Order = 4)] | ||
[property: MemoryPackOrder(4)] | ||
[property: Key(4)] | ||
[Id(4)] | ||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; | ||
|
||
public static implicit operator UserStorageDto(UserStorage userStorage) | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,30 @@ | ||
namespace Argon.Api.Grains.Persistence.States; | ||
|
||
using Contracts; | ||
using Mapster; | ||
using MemoryPack; | ||
|
||
[GenerateSerializer] | ||
[Serializable] | ||
[MemoryPackable] | ||
[Alias(nameof(UserStorageDto))] | ||
[GenerateMapper] | ||
public sealed partial record UserStorageDto | ||
{ | ||
[Id(0)] public Guid Id { get; set; } | ||
[Id(1)] public string Username { get; set; } = string.Empty; | ||
[Id(4)] public string AvatarUrl { get; set; } = string.Empty; | ||
[Id(2)] public DateTime CreatedAt { get; set; } | ||
[Id(3)] public DateTime UpdatedAt { get; set; } | ||
|
||
public static implicit operator UserResponse(UserStorageDto userStorageDto) | ||
{ | ||
return new UserResponse( | ||
userStorageDto.Id, | ||
userStorageDto.Username, | ||
userStorageDto.AvatarUrl, | ||
userStorageDto.CreatedAt, | ||
userStorageDto.UpdatedAt | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
namespace Argon.Api.Services; | ||
|
||
using Grains.Interfaces; | ||
using Contracts; | ||
using Grains.Interfaces; | ||
|
||
public class UserAuthorization(IGrainFactory grainFactory) : IUserAuthorization | ||
{ | ||
public async Task<AuthorizeResponse> AuthorizeAsync(AuthorizeRequest request) | ||
{ | ||
// TODO machineKey | ||
var token = await grainFactory.GetGrain<IUserManager>(request.username).Authenticate(request.password); | ||
return new AuthorizeResponse(token, [new ServerResponse(Guid.NewGuid(), "xuita", null)]); | ||
var token = await grainFactory.GetGrain<IUserManager>(request.username).Authorize(request.password); | ||
return new AuthorizeResponse(token); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
namespace Argon.Api.Services; | ||
|
||
using Contracts; | ||
using Grains.Interfaces; | ||
|
||
public class UserInteractionService( | ||
string username, // TODO to be injected | ||
IGrainFactory grainFactory | ||
) : IUserInteraction | ||
{ | ||
private readonly IUserManager userManager = grainFactory.GetGrain<IUserManager>(username); | ||
|
||
public async Task<UserResponse> GetMe() | ||
{ | ||
return await userManager.Get(); | ||
} | ||
|
||
public async Task<ServerResponse> CreateServer(CreateServerRequest request) | ||
{ | ||
return await userManager.CreateServer(request.Name, request.Description); | ||
} | ||
|
||
public async Task<List<ServerResponse>> GetServers() | ||
{ | ||
return (await userManager.GetServers()) | ||
.Select(x => (ServerResponse)x) | ||
.ToList(); | ||
} | ||
|
||
public async Task<List<ServerDetailsResponse>> GetServerDetails(ServerDetailsRequest request) | ||
{ | ||
return (await userManager.GetServerChannels(request.ServerId)) | ||
.Select(x => (ServerDetailsResponse)x) | ||
.ToList(); | ||
} | ||
|
||
public async Task<ChannelJoinResponse> JoinChannel(ChannelJoinRequest request) | ||
{ | ||
return new ChannelJoinResponse((await userManager.JoinChannel(request.ServerId, request.ChannelId)).value); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="ActualLab.Fusion" Version="9.5.52" /> | ||
<PackageReference Include="ActualLab.Fusion.Ext.Contracts" Version="9.5.52" /> | ||
<PackageReference Include="ActualLab.Generators" Version="9.5.52"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="MemoryPack.Generator" Version="1.21.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<PackageReference Include="ActualLab.Fusion" Version="9.5.52"/> | ||
<PackageReference Include="ActualLab.Fusion.Ext.Contracts" Version="9.5.52"/> | ||
<PackageReference Include="ActualLab.Generators" Version="9.5.52"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Mapster" Version="7.4.0"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Mapster integration needs configuration setup The codebase shows minimal Mapster usage with potential issues:
To properly integrate Mapster:
🔗 Analysis chainVerify Mapster usage across the codebase. The addition of Mapster looks good. Let's verify its integration with the existing codebase. 🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Description: Verify Mapster usage and configuration
# Expected: Find mapping configurations and usages to ensure proper setup
# Check for Mapster configuration classes
echo "Searching for Mapster configuration..."
rg -l "IRegister|TypeAdapterConfig"
# Check for Mapster usage
echo "Searching for Mapster usage..."
rg -l "using Mapster|\.Adapt<|\.Adapt\("
Length of output: 296 Script: #!/bin/bash
# Let's examine the actual usage in the found file
echo "Checking Mapster usage in UserStorageDto.cs:"
rg -A 5 -B 5 "using Mapster|\.Adapt<|\.Adapt\(" "src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs"
# Let's also check for any recent changes to this file
echo -e "\nChecking recent changes to UserStorageDto.cs:"
git log -p "src/Argon.Api/Grains.Persistence.States/UserStorageDto.cs"
# Let's verify if there are any mapping configurations in Program.cs or Startup.cs
echo -e "\nChecking for potential Mapster configuration in startup files:"
fd -e cs "Program|Startup" --exec rg -l "MapsterConfig|TypeAdapterConfig|RegisterMaps"
Length of output: 3164 Script: #!/bin/bash
# Let's check for the GenerateMapper attribute usage and any mapping configurations
echo "Checking for Mapster configuration attributes:"
rg -l "\[GenerateMapper\]"
# Let's check for UserResponse to understand the mapping context
echo -e "\nChecking UserResponse definition:"
rg -A 10 "record UserResponse"
# Let's check for any Mapster-related files in the solution
echo -e "\nChecking for Mapster-related files:"
fd -e cs -e cs.generated "Mapper|MapRegister|TypeAdapter"
Length of output: 1234 |
||
<PackageReference Include="MemoryPack.Generator" Version="1.21.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,6 @@ | ||
namespace Argon.Contracts; | ||
|
||
using System.Runtime.Serialization; | ||
using ActualLab.Fusion; | ||
using ActualLab.Rpc; | ||
using MemoryPack; | ||
using MessagePack; | ||
|
@@ -11,19 +10,25 @@ public interface IUserAuthorization : IRpcService | |
Task<AuthorizeResponse> AuthorizeAsync(AuthorizeRequest request); | ||
} | ||
|
||
[DataContract, MemoryPackable(GenerateType.VersionTolerant), MessagePackObject] | ||
[DataContract] | ||
[MemoryPackable(GenerateType.VersionTolerant)] | ||
[MessagePackObject] | ||
public sealed partial record AuthorizeRequest( | ||
[property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] string username, | ||
[property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] string password, | ||
[property: DataMember(Order = 2), MemoryPackOrder(2), Key(2)] string machineKey); | ||
[property: DataMember(Order = 0)] | ||
[property: MemoryPackOrder(0)] | ||
[property: Key(0)] | ||
string username, | ||
[property: DataMember(Order = 1)] | ||
[property: MemoryPackOrder(1)] | ||
[property: Key(1)] | ||
string password, | ||
[property: DataMember(Order = 2)] | ||
[property: MemoryPackOrder(2)] | ||
[property: Key(2)] | ||
string machineKey); | ||
Comment on lines
+13
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Codebase verification Add input validation and security measures for sensitive data Based on the codebase analysis:
Required changes:
[Required, StringLength(100)]
string username,
[Required, StringLength(256)]
string password,
[Required]
string machineKey
🔗 Analysis chainAdd input validation and security measures. The record handles sensitive data (username, password) but lacks input validation. Consider:
🏁 Scripts executedThe following scripts were executed for the analysis: Script: #!/bin/bash
# Check if sensitive data is properly handled across the codebase
# Look for potential logging of AuthorizeRequest
rg -A 5 "AuthorizeRequest.*log|log.*AuthorizeRequest"
# Check for any validation attributes usage in similar contexts
ast-grep --pattern 'attribute { name: /Required|StringLength|MinLength/ }'
Length of output: 3585 Script: #!/bin/bash
# Let's check for any existing validation attributes or security measures in the codebase
rg -A 3 "\[(?:Required|StringLength|MinLength|MaxLength|RegularExpression|SensitiveData)\]"
# Check for any password-related validation or security measures
rg -A 5 "password.*validation|validate.*password"
# Look for logging configurations or sensitive data handling patterns
rg -A 3 "ILogger|LoggerFactory|SensitiveData|SecureString"
# Check for any existing validation in the authentication flow
ast-grep --pattern 'class { name: /.*Auth.*/ }'
Length of output: 1560 Script: #!/bin/bash
# Let's check the actual implementation of authentication/authorization
rg -A 10 "class.*UserAuthorization"
# Check for any existing validation in the request handling
rg -A 10 "AuthorizeRequest.*async"
# Look for any security-related configurations
rg -A 5 "security|authentication|authorization" src/Argon.Api/Program.cs src/Argon.Api/Startup.cs
# Check if there's any request validation middleware
rg -A 5 "middleware.*validation|validate.*request"
Length of output: 1047 |
||
|
||
|
||
public sealed partial record ServerResponse( | ||
[property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] Guid serverId, | ||
[property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] string serverName, | ||
[property: DataMember(Order = 2), MemoryPackOrder(2), Key(2)] string? avatarFileId | ||
); | ||
|
||
public sealed partial record AuthorizeResponse( | ||
[property: DataMember(Order = 0), MemoryPackOrder(0), Key(0)] string token, | ||
[property: DataMember(Order = 1), MemoryPackOrder(1), Key(1)] List<ServerResponse> servers); | ||
public sealed record AuthorizeResponse( | ||
[property: DataMember(Order = 0)] | ||
[property: MemoryPackOrder(0)] | ||
[property: Key(0)] | ||
string token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
Method name mismatch between interface and usage
The verification revealed inconsistencies in the codebase:
[Alias("Authenticate")]
but namedAuthorize
authenticate
This indicates that:
Authorize
is inconsistent with both its alias and usageRecommendation:
Authenticate
to maintain consistency with its alias, endpoint name, and actual functionality[Authorize]
attribute usage in the controller)🔗 Analysis chain
Reconsider renaming
Authenticate
toAuthorize
method.The renaming of this method could lead to confusion as authentication and authorization are distinct security concepts:
Since this method accepts a password and likely returns an authentication token, it appears to be performing authentication rather than authorization. Consider keeping the original
Authenticate
name to maintain clarity and accurate terminology.Let's verify the impact of this rename across the codebase:
Let me gather more context about the actual implementation and usage to make a more informed decision.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
Length of output: 935
Script:
Length of output: 2493