@@ -34,14 +34,15 @@ public async Task<ActionResult<AuthResponse>> Authenticate([FromBody] DTO.LoginR
3434 if ( string . IsNullOrWhiteSpace ( req . UserName ) || string . IsNullOrWhiteSpace ( req . Password ) )
3535 return BadRequest ( "Username and password are required." ) ;
3636
37- // Validate user (plain text as per your seed)
3837 var user = await _db . Users
3938 . Include ( u => u . UserRoles )
4039 . ThenInclude ( ur => ur . Role )
41- . FirstOrDefaultAsync ( u => u . UserName == req . UserName && u . Password == BCrypt . Net . BCrypt . HashPassword ( req . Password ) ) ;
40+ . FirstOrDefaultAsync ( u => u . UserName == req . UserName ) ;
4241
43- if ( user == null )
42+ if ( user == null || ! BCrypt . Net . BCrypt . Verify ( req . Password , user . Password ) )
43+ {
4444 return Unauthorized ( "Invalid credentials." ) ;
45+ }
4546
4647 // Collect roles & function codes for claims (optional but handy)
4748 var roleIds = user . UserRoles . Select ( ur => ur . RoleId ) . ToList ( ) ;
@@ -80,33 +81,65 @@ public async Task<ActionResult<UserPermissionsDto>> GetPermissions(int userId)
8081
8182 private async Task < UserPermissionsDto > BuildPermissionsForUser ( int userId )
8283 {
83- var user = await _db . Users . FirstAsync ( u => u . Id == userId ) ;
84-
85- var categories = await _db . Categories
86- . Select ( c => new CategoryDto (
87- c . Id ,
88- c . Name ,
89- c . Modules
90- . Select ( m => new ModuleDto (
91- m . Id , m . Name , m . Area , m . Controller , m . Action ,
92- m . Functions
93- . Where ( f => f . RoleFunctions
94- . Any ( rf => rf . Role . UserRoles . Any ( ur => ur . UserId == userId ) ) )
95- . Select ( f => new FunctionDto ( f . Id , f . Code , f . DisplayName ) )
84+ // Fetch the user (for name in DTO)
85+ var user = await _db . Users . AsNoTracking ( ) . FirstAsync ( u => u . Id == userId ) ;
86+
87+ // 1) Get all (Category, Module, Function) triples the user is allowed to access.
88+ // This is fully translatable SQL: joins + where.
89+ var triples = await (
90+ from ur in _db . UserRoles . AsNoTracking ( )
91+ where ur . UserId == userId
92+ join rf in _db . RoleFunctions . AsNoTracking ( ) on ur . RoleId equals rf . RoleId
93+ join f in _db . Functions
94+ . Include ( x => x . Module )
95+ . ThenInclude ( m => m . Category )
96+ . AsNoTracking ( )
97+ on rf . FunctionId equals f . Id
98+ select new
99+ {
100+ CategoryId = f . Module . Category . Id ,
101+ CategoryName = f . Module . Category . Name ,
102+ ModuleId = f . Module . Id ,
103+ ModuleName = f . Module . Name ,
104+ f . Module . Area ,
105+ f . Module . Controller ,
106+ f . Module . Action ,
107+ FunctionId = f . Id ,
108+ f . Code ,
109+ f . DisplayName
110+ }
111+ ) . ToListAsync ( ) ;
112+
113+ // 2) Group in memory to shape the hierarchical DTOs.
114+ var categoryDtos = triples
115+ . GroupBy ( t => new { t . CategoryId , t . CategoryName } )
116+ . Select ( cg => new CategoryDto (
117+ cg . Key . CategoryId ,
118+ cg . Key . CategoryName ,
119+ cg . GroupBy ( t => new { t . ModuleId , t . ModuleName , t . Area , t . Controller , t . Action } )
120+ . Select ( mg => new ModuleDto (
121+ mg . Key . ModuleId ,
122+ mg . Key . ModuleName ,
123+ mg . Key . Area ,
124+ mg . Key . Controller ,
125+ mg . Key . Action ,
126+ mg . GroupBy ( x => new { x . FunctionId , x . Code , x . DisplayName } ) // distinct functions
127+ . Select ( g => new FunctionDto ( g . Key . FunctionId , g . Key . Code , g . Key . DisplayName ) )
128+ . OrderBy ( f => f . Code )
96129 . ToList ( )
97130 ) )
98- . Where ( md => md . Functions . Any ( ) )
131+ . OrderBy ( m => m . Name )
99132 . ToList ( )
100133 ) )
101- . Where ( cd => cd . Modules . Any ( ) )
102- . ToListAsync ( ) ;
134+ . OrderBy ( c => c . Name )
135+ . ToList ( ) ;
103136
104- return new UserPermissionsDto ( user . Id , user . UserName , categories ) ;
137+ return new UserPermissionsDto ( user . Id , user . UserName , categoryDtos ) ;
105138 }
106139
107140 private string GenerateJwt ( AppUser user , IEnumerable < string > roles , IEnumerable < string > functionCodes , out DateTime expiresAtUtc )
108141 {
109- var key = new SymmetricSecurityKey ( Encoding . UTF8 . GetBytes ( _jwt . Key ) ) ;
142+ var key = new SymmetricSecurityKey ( Encoding . UTF8 . GetBytes ( _jwt . KeyBase64 ) ) ;
110143 var creds = new SigningCredentials ( key , SecurityAlgorithms . HmacSha256 ) ;
111144
112145 var claims = new List < Claim >
0 commit comments