Skip to content

Commit f97c6c3

Browse files
committed
2 parents 9b7c1e2 + 560f1a3 commit f97c6c3

8 files changed

Lines changed: 261 additions & 3 deletions

File tree

Sources/EasyExtensions.AspNetCore.Authorization/Controllers/BaseAuthController.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,31 @@ public abstract class BaseAuthController(
3131
/// </summary>
3232
public IPAddress RequestIP => IPAddress.Parse(Request.GetRemoteAddress());
3333

34+
/// <summary>
35+
/// Logs out the current user by revoking the refresh token and removing the authentication cookie.
36+
/// </summary>
37+
/// <remarks>This endpoint supports GET, POST, and DELETE HTTP methods. If a refresh token cookie
38+
/// is present, it is revoked and deleted. If no refresh token is found, the operation completes without
39+
/// error.</remarks>
40+
/// <returns>An <see cref="IActionResult"/> indicating the result of the logout operation. Returns a success message if
41+
/// the user was logged out.</returns>
42+
[HttpGet("logout")]
43+
[HttpPost("logout")]
44+
[HttpDelete("logout")]
45+
public async Task<IActionResult> Logout([FromQuery] string? token = "")
46+
{
47+
if (Request.Cookies.TryGetValue(CookieRefreshTokenName, out string? refreshToken))
48+
{
49+
await RevokeRefreshTokenAsync(refreshToken);
50+
Response.Cookies.Delete(CookieRefreshTokenName);
51+
}
52+
if (!string.IsNullOrWhiteSpace(token))
53+
{
54+
await RevokeRefreshTokenAsync(token);
55+
}
56+
return Ok("Logged out successfully");
57+
}
58+
3459
/// <summary>
3560
/// Changes the password for the currently authenticated user.
3661
/// </summary>
@@ -248,6 +273,15 @@ public async Task<IActionResult> LoginWithGoogle([FromQuery] string token)
248273
/// <returns>A task that represents the asynchronous operation.</returns>
249274
public abstract Task SaveAndRevokeRefreshTokenAsync(Guid userId, string oldRefreshToken, string newRefreshToken, AuthType authType);
250275

276+
/// <summary>
277+
/// Revokes the specified refresh token, invalidating it for future use.
278+
/// </summary>
279+
/// <remarks>After revocation, the specified refresh token can no longer be used to obtain new
280+
/// access tokens. This method is typically used to log out a user or to respond to a security event.</remarks>
281+
/// <param name="refreshToken">The refresh token to revoke. Cannot be null or empty.</param>
282+
/// <returns>A task that represents the asynchronous revoke operation.</returns>
283+
public abstract Task RevokeRefreshTokenAsync(string refreshToken);
284+
251285
/// <summary>
252286
/// Asynchronously retrieves the unique identifier of a user associated with the specified refresh token.
253287
/// </summary>

Sources/EasyExtensions.AspNetCore.Sentry/Factories/UserFactory.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ public class UserFactory(IHttpContextAccessor httpContextAccessor) : ISentryUser
2323
return null;
2424
}
2525
var context = httpContextAccessor.HttpContext;
26+
if (context.User?.Identity == null || !context.User.Identity.IsAuthenticated)
27+
{
28+
return null;
29+
}
2630
var claims = httpContextAccessor.HttpContext.User.Claims;
2731
int userId = httpContextAccessor.HttpContext.User.TryGetId();
2832
bool hasUserId = httpContextAccessor.HttpContext.User.TryGetUserId(out Guid guidUserId);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using EasyExtensions.EntityFrameworkCore.Abstractions;
2+
using EasyExtensions.Models.Enums;
3+
using Microsoft.EntityFrameworkCore;
4+
using System.ComponentModel.DataAnnotations.Schema;
5+
using System.Text;
6+
7+
namespace EasyExtensions.EntityFrameworkCore.Npgsql.Database
8+
{
9+
/// <summary>
10+
/// Represents a base user account with identity, contact, and profile information.
11+
/// </summary>
12+
/// <remarks>This class provides common user properties for authentication, authorization, and
13+
/// personalization scenarios. It is intended to be used as a base type for user entities in applications that
14+
/// require user management. The class enforces unique email addresses and supports user preferences, permissions,
15+
/// and role-based access control.</remarks>
16+
[Table("users")]
17+
[Index(nameof(Email), IsUnique = true)]
18+
public class BasePostgresUser : BaseEntity<Guid>
19+
{
20+
/// <summary>
21+
/// Gets the full name, consisting of the first and last name combined with a space.
22+
/// </summary>
23+
[NotMapped]
24+
public string FullName => GetFullName();
25+
26+
private string GetFullName()
27+
{
28+
var sb = new StringBuilder();
29+
sb.Append(FirstName);
30+
if (!string.IsNullOrWhiteSpace(MiddleName))
31+
{
32+
sb.Append(' ').Append(MiddleName);
33+
}
34+
sb.Append(' ').Append(LastName);
35+
return sb.ToString().Trim();
36+
}
37+
38+
/// <summary>
39+
/// Gets or sets the first name of the person.
40+
/// </summary>
41+
[Column("first_name", TypeName = "citext")]
42+
public string FirstName { get; set; } = null!;
43+
44+
/// <summary>
45+
/// Gets or sets the middle name of the person.
46+
/// </summary>
47+
[Column("middle_name", TypeName = "citext")]
48+
public string? MiddleName { get; set; } = null!;
49+
50+
/// <summary>
51+
/// Gets or sets the last name of the person.
52+
/// </summary>
53+
[Column("last_name", TypeName = "citext")]
54+
public string LastName { get; set; } = null!;
55+
56+
/// <summary>
57+
/// Gets or sets the avatar image data in WebP format.
58+
/// </summary>
59+
[Column("avatar_webp_bytes")]
60+
public byte[]? AvatarWebPBytes { get; set; }
61+
62+
/// <summary>
63+
/// Gets or sets the email address associated with the entity.
64+
/// </summary>
65+
[Column("email", TypeName = "citext")]
66+
public string Email { get; set; } = null!;
67+
68+
/// <summary>
69+
/// Gets or sets the phone number associated with the entity.
70+
/// </summary>
71+
[Column("phone_number")]
72+
public long? PhoneNumber { get; set; }
73+
74+
/// <summary>
75+
/// Gets or sets the collection of user preferences as key-value pairs.
76+
/// </summary>
77+
/// <remarks>Each entry in the dictionary represents a user preference, where the key is the
78+
/// preference name and the value is its corresponding setting. Preference names and values are case-sensitive.
79+
/// Modifying this collection updates the user's stored preferences.</remarks>
80+
[Column("preferences")]
81+
public Dictionary<string, string> Preferences { get; set; } = [];
82+
83+
/// <summary>
84+
/// Gets or sets the collection of permissions assigned to the entity.
85+
/// </summary>
86+
[Column("permissions")]
87+
public ICollection<string> Permissions { get; set; } = [];
88+
89+
/// <summary>
90+
/// Gets or sets a value indicating whether the entity is active.
91+
/// </summary>
92+
[Column("is_active")]
93+
public bool IsActive { get; set; } = true;
94+
95+
/// <summary>
96+
/// Gets or sets the password hash in PHC (Password Hashing Competition) string format.
97+
/// </summary>
98+
/// <remarks>The value is typically a string produced by a password hashing algorithm that follows
99+
/// the PHC string format, which includes information about the algorithm, parameters, salt, and hash. This
100+
/// property may be null if no password is set.</remarks>
101+
[Column("password_phc")]
102+
public string? PasswordPhc { get; set; }
103+
104+
/// <summary>
105+
/// Gets or sets the role assigned to the user.
106+
/// </summary>
107+
[Column("role")]
108+
public UserRole Role { get; set; }
109+
110+
/// <summary>
111+
/// Gets or sets the token used to authorize a password reset request for the user.
112+
/// </summary>
113+
[Column("reset_password_token", TypeName = "citext")]
114+
public string? ResetPasswordToken { get; set; }
115+
}
116+
}

Sources/EasyExtensions.EntityFrameworkCore.Npgsql/EasyExtensions.EntityFrameworkCore.Npgsql.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
3535
</ItemGroup>
3636
<ItemGroup>
37+
<ProjectReference Include="..\EasyExtensions.EntityFrameworkCore\EasyExtensions.EntityFrameworkCore.csproj" />
3738
<ProjectReference Include="..\EasyExtensions\EasyExtensions.csproj" />
3839
</ItemGroup>
3940
</Project>

Sources/EasyExtensions.EntityFrameworkCore.Npgsql/Providers/PostgresConnectionStringProvider.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
using EasyExtensions.EntityFrameworkCore.Npgsql.Builders;
33
using Microsoft.Extensions.Configuration;
44
using Npgsql;
5-
using System;
6-
using System.Collections.Generic;
7-
using System.Text;
85

96
namespace EasyExtensions.EntityFrameworkCore.Npgsql.Providers
107
{
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
5+
namespace EasyExtensions.Extensions
6+
{
7+
/// <summary>
8+
/// Provides extension methods for working with enumeration (enum) values.
9+
/// </summary>
10+
/// <remarks>This static class contains utility methods that extend the functionality of enum types,
11+
/// enabling more convenient or user-friendly operations when working with enumerations.</remarks>
12+
public static class EnumExtensions
13+
{
14+
/// <summary>
15+
/// Converts the value of the specified enumeration to a user-friendly string with spaces inserted before
16+
/// uppercase letters.
17+
/// </summary>
18+
/// <remarks>This method is useful for displaying enumeration values in a more readable format,
19+
/// such as in user interfaces or logs. For example, an enum value named "OrderStatusPendingApproval" would be
20+
/// converted to "Order Status Pending Approval".</remarks>
21+
/// <param name="e">The enumeration value to convert to a nicely formatted string.</param>
22+
/// <returns>A string representation of the enumeration value with spaces inserted before uppercase letters. Returns the
23+
/// enumeration name as-is if no formatting is needed.</returns>
24+
public static string ToNiceString(this Enum e)
25+
{
26+
string enumString = e.ToString();
27+
IEnumerable<char> chars = enumString.SelectMany((c, i) =>
28+
{
29+
if (i > 0 && char.IsUpper(c))
30+
{
31+
return new char[] { ' ', c };
32+
}
33+
return new[] { c };
34+
});
35+
return new string(chars.ToArray());
36+
}
37+
}
38+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using EasyExtensions.Models.Enums;
2+
using System;
3+
4+
namespace EasyExtensions.Models.Dto
5+
{
6+
/// <summary>
7+
/// Represents the base data transfer object for a user, including common user profile information and role
8+
/// assignment.
9+
/// </summary>
10+
/// <remarks>This class provides fundamental user properties for use in API requests and responses. It is
11+
/// intended to be inherited by more specialized user DTOs as needed. The properties include identification, contact
12+
/// information, activation status, and role assignment.</remarks>
13+
public class BaseUserDto : BaseDto<Guid>
14+
{
15+
/// <summary>
16+
/// Gets or sets the first name of the person.
17+
/// </summary>
18+
public string FirstName { get; set; } = null!;
19+
20+
/// <summary>
21+
/// Gets or sets the last name of the person.
22+
/// </summary>
23+
public string LastName { get; set; } = null!;
24+
25+
/// <summary>
26+
/// Gets or sets the email address associated with the user.
27+
/// </summary>
28+
public string Email { get; set; } = null!;
29+
30+
/// <summary>
31+
/// Gets or sets a value indicating whether the instance is currently active.
32+
/// </summary>
33+
public bool IsActive { get; set; }
34+
35+
/// <summary>
36+
/// Gets or sets the role assigned to the user.
37+
/// </summary>
38+
public UserRole Role { get; set; }
39+
}
40+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
namespace EasyExtensions.Models.Enums
2+
{
3+
/// <summary>
4+
/// Specifies the set of roles that can be assigned to a user within the system.
5+
/// </summary>
6+
/// <remarks>Use this enumeration to define user permissions and access levels. The roles represent
7+
/// increasing levels of responsibility and access, from standard users to administrators. The meaning and usage of
8+
/// the 'Custom' role may vary depending on application-specific requirements.</remarks>
9+
public enum UserRole
10+
{
11+
/// <summary>
12+
/// Indicates that the value is unknown or not specified.
13+
/// </summary>
14+
/// <remarks>Use this value when the actual value cannot be determined or is not applicable. This
15+
/// is typically used as a default or placeholder value.</remarks>
16+
Unknown = 1,
17+
18+
/// <summary>
19+
/// Represents a standard user account with typical permissions and access rights.
20+
/// </summary>
21+
User = 2,
22+
23+
/// <summary>
24+
/// Specifies an administrator role with elevated permissions.
25+
/// </summary>
26+
Admin = 4,
27+
}
28+
}

0 commit comments

Comments
 (0)