Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ebf315e
create extension for authentication from keycloack in swagger, also e…
PooyaAbbasi Aug 18, 2025
f6572d5
add some authorization policy
PooyaAbbasi Aug 18, 2025
24fbf90
add authentication temp in .http
PooyaAbbasi Aug 18, 2025
dda6e6b
Merge pull request #2 from Star-Academy/frontend/authentication
amirbarari Aug 19, 2025
7fe4069
add auth controllers
PooyaAbbasi Aug 19, 2025
0a3c4d9
Merge branch 'backend-develop' of https://github.com/Star-Academy/Sum…
PooyaAbbasi Aug 19, 2025
08c46d6
authentication and polices added successfully
PooyaAbbasi Aug 19, 2025
565a2a4
separate keycloak authentication middleware.
PooyaAbbasi Aug 19, 2025
6161e56
add service for authentication with keclaok
PooyaAbbasi Aug 20, 2025
bd34a8c
Admin controller
SabaSaadatnia Aug 20, 2025
43af53a
Admin controller
SabaSaadatnia Aug 20, 2025
6f19ad4
Merge remote-tracking branch 'origin/backend-develop-admin-features' …
PooyaAbbasi Aug 22, 2025
962b631
clean and seperate services of keycloak authentication and authorizat…
PooyaAbbasi Aug 22, 2025
2fed0ec
Merge branch 'backend-develop-admin-features' into backend-develop
PooyaAbbasi Aug 23, 2025
4d3a5e0
add all endpoints and services for user management
PooyaAbbasi Aug 24, 2025
0f12329
redesing architecture of the project files to be more cleaned clean
PooyaAbbasi Aug 24, 2025
c35fa2a
fix some bugs in logout and dto
PooyaAbbasi Aug 25, 2025
3292b7f
add docker file to dockerization, and fix up problems to authenticati…
PooyaAbbasi Aug 25, 2025
96c8430
Merge branch 'main' into backend-develop
PooyaAbbasi Aug 27, 2025
b69baac
fix up cookie same site parameter to be adaptable with ASPNETCORE_ENV…
PooyaAbbasi Aug 27, 2025
9797ea9
bug fix in extraction of profile from token
PooyaAbbasi Aug 29, 2025
01e8435
change role extractore to extract user roles with user id of it.
PooyaAbbasi Aug 29, 2025
b9e6f49
fix: KeyCloakAdminClient for get users is error prone now.
SabaSaadatnia Aug 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions etl_backend/etl_backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/.idea
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
98 changes: 98 additions & 0 deletions etl_backend/etl_backend/Api/Controllers/AdminController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using etl_backend.Application.Abstraction;
using etl_backend.Application.Dtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace etl_backend.Api.Controllers;

[ApiController]
[Route("api/admin")]
[Authorize(Policy = "RequireSysAdmin")]
public class AdminController : ControllerBase
{
private readonly IGetAllUsersService _getAllUsersService;
private readonly IGetUserByIdService _getUserByIdService;
private readonly ICreateUserService _createUserService;
private readonly IEditUserService _editUserService;
private readonly IDeleteUserService _deleteUserService;
private readonly IEditUserRolesService _editUserRolesService;
private readonly IGetRolesList _getRolesList;

public AdminController(
IGetAllUsersService getAllUsersService,
IGetUserByIdService getUserByIdService,
ICreateUserService createUserService,
IEditUserService editUserService,
IDeleteUserService deleteUserService,
IEditUserRolesService editUserRolesService,
IGetRolesList getRolesList)
{
_getAllUsersService = getAllUsersService;
_getUserByIdService = getUserByIdService;
_createUserService = createUserService;
_editUserService = editUserService;
_deleteUserService = deleteUserService;
_editUserRolesService = editUserRolesService;
_getRolesList = getRolesList;
}

[HttpGet("users")]
public async Task<ActionResult<IEnumerable<UserWithRolesDto>>> GetAllUsers(CancellationToken cancellationToken)
{
var users = await _getAllUsersService.ExecuteAsync(cancellationToken);
return Ok(users);
}

[HttpGet("users/{userId}")]
public async Task<ActionResult<UserWithRolesDto>> GetUserById(string userId, CancellationToken cancellationToken)
{

var user = await _getUserByIdService.ExecuteAsync(userId, cancellationToken);
return Ok(user);
}

[HttpPost("users")]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] UserCreateDto createDto, CancellationToken cancellationToken)
{
var createdUser = await _createUserService.ExecuteAsync(createDto, cancellationToken);
return createdUser;
}

[HttpPut("users/{userId}")]
public async Task<IActionResult> EditUser(string userId, [FromBody] EditUserRequestDto requestDto, CancellationToken cancellationToken)
{

await _editUserService.ExecuteAsync(userId, requestDto, cancellationToken);
return NoContent();
}

[HttpDelete("users/{userId}")]
public async Task<IActionResult> DeleteUser(string userId, CancellationToken cancellationToken)
{
await _deleteUserService.ExecuteAsync(userId, cancellationToken);
return NoContent();
}

[HttpPut("users/{userId}/roles")]
public async Task<ActionResult<UserWithRolesDto>> EditUserRoles(
string userId,
[FromBody] EditUserRolesRequestDto requestDto,
CancellationToken cancellationToken)
{
var updatedUser = await _editUserRolesService.ExecuteAsync(
userId,
requestDto.RolesToAdd,
requestDto.RolesToRemove,
cancellationToken);

return Ok(updatedUser);
}

[HttpGet("roles")]
public async Task<ActionResult<IEnumerable<RoleDto>>> GetAllRoles(CancellationToken cancellationToken)
{
var allRoles = await _getRolesList.ExecuteAsync(cancellationToken);
return Ok(allRoles);
}
}

73 changes: 73 additions & 0 deletions etl_backend/etl_backend/Api/Controllers/AuthController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using etl_backend.Application.Abstraction;
using etl_backend.Application.KeycalokAuth.Abstraction;
using etl_backend.Application.KeycalokAuth.Dtos;
using etl_backend.Configuration;
using etl_backend.DTO;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;

namespace etl_backend.Api.Controllers;

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{

private readonly ITokenCookieService _tokenCookieService;
private readonly IKeycloakAuthService _keycloakAuthService;
private readonly IKeycloakLogOutUser _keycloakLogOutUser;
private readonly ITokenProfileExtractor _tokenProfileExtractor;

public AuthController(ITokenCookieService tokenCookieService, IKeycloakAuthService keycloakAuthService, IKeycloakLogOutUser keycloakLogOutUser, ITokenProfileExtractor tokenProfileExtractor)
{
_tokenCookieService = tokenCookieService;
_keycloakAuthService = keycloakAuthService;
_keycloakLogOutUser = keycloakLogOutUser;
_tokenProfileExtractor = tokenProfileExtractor;

}

[HttpPost("login")]
public IActionResult Login([FromBody] LoginRequestBodyDto request)
{

var url = _keycloakAuthService.GenerateLoginUrl(request.RedirectUrl);

return Ok(new { redirectUrl = url });
}

[HttpPost("token")]
public async Task<IActionResult> SetToken([FromBody] SetTokenRequestDto request)
{

var tokens = await _keycloakAuthService.ExchangeCodeForTokensAsync(request.Code, request.RedirectUrl);

_tokenCookieService.SetTokens(Response, tokens);

return Ok(new { message = "Tokens set successfully" });
}

[HttpPost("logout")]
public async Task<IActionResult> Logout()
{

var currentUser = await _tokenProfileExtractor.ExtractProfile(User);
var userId = currentUser.Id;
var logoutSuccessfully = await _keycloakLogOutUser.LogOutAsynk(userId, CancellationToken.None);
return logoutSuccessfully ? NoContent() : BadRequest();

}

[HttpGet("me")]
[Authorize(Policy = "RequireSysAdmin")]
public IActionResult GetUserInfo()
{
var claims = User.Claims
.Select(c => new { c.Type, c.Value })
.ToList();
return Ok(claims);
}

}
26 changes: 26 additions & 0 deletions etl_backend/etl_backend/Api/Controllers/PasswordController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using etl_backend.Application.KeycalokAuth.Abstraction;
using Microsoft.AspNetCore.Mvc;

namespace etl_backend.Api.Controllers;

[ApiController]
[Route("api/password")]
public class PasswordController : ControllerBase
{
private readonly IKeycloakAuthService _keycloakAuthService;

public PasswordController(IKeycloakAuthService keycloakAuthService)
{
_keycloakAuthService = keycloakAuthService;

}

[HttpGet("change-password-url")]
public IActionResult ChangePassword()
{
return Ok(new {
changePasswordUrl = _keycloakAuthService.GenerateChangePasswordUrlPage()
});
}
}

41 changes: 41 additions & 0 deletions etl_backend/etl_backend/Api/Controllers/ProfileController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using etl_backend.Application.Abstraction;
using etl_backend.Application.Dtos;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace etl_backend.Api.Controllers;

[ApiController]
[Route("api/users")]
public class ProfileController : ControllerBase
{

private readonly ITokenProfileExtractor _tokenProfileExtractor;
private readonly IEditUserService _editUserService;

public ProfileController(ITokenProfileExtractor tokenProfileExtractor, IEditUserService editUserService)
{
_tokenProfileExtractor = tokenProfileExtractor;
_editUserService = editUserService;
}

[HttpGet("me")]
[Authorize]
public async Task<ActionResult<UserWithRolesDto>> GetProfile(CancellationToken cancellationToken)
{

var profile = await _tokenProfileExtractor.ExtractProfile(User);
return Ok(profile);

}

[HttpPut("me")]
[Authorize]
public async Task<IActionResult> UpdateProfile([FromBody] EditUserRequestDto profile, CancellationToken cancellationToken)
{
var currentUser = await _tokenProfileExtractor.ExtractProfile(User);
var userId = currentUser.Id;
await _editUserService.ExecuteAsync(userId, profile, cancellationToken);
return NoContent();
}
}
6 changes: 6 additions & 0 deletions etl_backend/etl_backend/Api/Dtos/LoginRequestBodyDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace etl_backend.DTO;

public class LoginRequestBodyDto
{
public string RedirectUrl { get; set; } = string.Empty;
}
7 changes: 7 additions & 0 deletions etl_backend/etl_backend/Api/Dtos/SetTokenRequestDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace etl_backend.DTO;

public class SetTokenRequestDto
{
public string Code { get; set; } = string.Empty;
public string RedirectUrl { get; set; } = string.Empty;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;

public interface ICreateUserService
{
Task<UserDto> ExecuteAsync(UserCreateDto newUser, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace etl_backend.Application.Abstraction;

public interface IDeleteUserService
{
Task ExecuteAsync(string userId, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;

public interface IEditUserRolesService
{
Task<UserWithRolesDto> ExecuteAsync(
string userId,
IEnumerable<RoleDto> rolesToAdd,
IEnumerable<RoleDto> rolesToRemove,
CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;

public interface IEditUserService
{
Task ExecuteAsync(string userId, EditUserRequestDto userToUpdate, CancellationToken cancellationToken);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;
public interface IGetAllUsersService
{
Task<IEnumerable<UserWithRolesDto>> ExecuteAsync(CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;

public interface IGetRolesList
{
Task<IEnumerable<RoleDto>> ExecuteAsync(CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;


public interface IGetUserByIdService
{
Task<UserWithRolesDto> ExecuteAsync(string userId, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Security.Claims;
using etl_backend.Application.Dtos;

namespace etl_backend.Application.Abstraction;

public interface IRoleExtractor
{
Task<IEnumerable<RoleDto>> ExtractRoles(ClaimsPrincipal user, string scope, string key);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using etl_backend.Application.KeycalokAuth.Dtos;

namespace etl_backend.Application.Abstraction;

public interface ITokenCookieService
{
void SetTokens(HttpResponse response, TokenResponseDto tokenResponse);
void RemoveTokens(HttpResponse response);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace etl_backend.Application.Abstraction;

public interface ITokenExpirationChecker
{
bool IsAccessTokenExpired(string accessToken, TimeSpan clockSkew, string expClaimType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace etl_backend.Application.Abstraction;

public interface ITokenExtractor
{
string? GetAccessToken(HttpRequest request, string accessCookieName);
string? GetRefreshToken(HttpRequest request, string refreshCookieName);
}
Loading