Compare commits

13 Commits

Author SHA1 Message Date
Developer 02
3832351dd1 add Jenkinsfile
Some checks failed
AppStd/WorkFlow/pipeline/head There was a failure building this commit
2025-05-22 16:03:12 +02:00
Developer 02
99237cbecc refactor(IISProfile): Verzeichnis aktualisiert, um die Namenskonventionen für digitale Daten anzuwenden 2025-03-25 15:38:33 +01:00
Developer 02
cb2edffe91 chore: Separate IIS-Konfiguration für .Net 7 und 8 hinzufügen 2025-03-24 16:12:39 +01:00
Developer 02
4d3768248e chore: IIS-Veröffentlichungsprofile getrennt nach Framework hinzufügen 2025-03-13 12:06:56 +01:00
Developer 02
fb38bc1fd4 chore(API): Hinzufügen von .net 7-Unterstützung für API 2025-03-13 10:12:55 +01:00
Developer 02
10b557374d chore: Hinzufügen von .net 7-Unterstützung für Domäne, Infrastruktur und Anwendung 2025-03-13 10:04:40 +01:00
Developer 02
f266e6728f chore: Upgrade auf DigitalData.Auth.Client 1.3.3 2025-03-13 09:29:07 +01:00
Developer 02
3373fceef3 chore(API): Hochgestuft auf 1.1.0 2025-03-11 17:17:08 +01:00
Developer 02
f7eaa0f7de refactor: IssuerSigningKeyResolver wurde aktualisiert, um die Konfiguration über serviceProvider anstelle eines separaten öffentlichen Schlüssels zu ermöglichen. 2025-03-11 16:22:54 +01:00
Developer 02
d5b1ee41a0 fix: Arranged auth Authentication Scheme 2025-03-11 10:48:04 +01:00
Developer 02
c3f5d90b6a refactor(PlaceholderAuthController): Update auf Login nur über Body und ohne Cookie 2025-03-11 10:32:28 +01:00
Developer 02
753eb18b71 updated(AuthController): Aktualisiert, um als Platzhalter für auth api in swagger zu funktionieren.
- umbenennen PlaceholderAuthController
2025-03-11 10:17:27 +01:00
Developer 02
17d8373739 feat: JwtBearerEvents hinzugefügt, um Token aus Cookie oder Query-String lesen zu können 2025-03-11 09:36:32 +01:00
35 changed files with 554 additions and 469 deletions

View File

@@ -1,154 +0,0 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using DigitalData.UserManager.Application.Contracts;
using DigitalData.UserManager.Application.DTOs.User;
using Microsoft.AspNetCore.Authorization;
using DigitalData.UserManager.Application;
using DigitalData.Core.Abstractions.Application;
using Microsoft.Extensions.Localization;
using DigitalData.Core.DTO;
using WorkFlow.API.Models;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers
{
//TODO: implement up-to-date AuthController in UserManager
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
public class AuthController(IUserService userService, IDirectorySearchService dirSearchService, IStringLocalizer<Resource> localizer, ILogger<AuthController> logger) : ControllerBase
{
[AllowAnonymous]
[HttpGet("check")]
public IActionResult CheckAuthentication()
{
try
{
return Ok(User.Identity?.IsAuthenticated ?? false);
}
catch(Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[NonAction]
public async Task<IActionResult> Login(UserReadDto user)
{
// Create claimsIdentity
var claimsIdentity = new ClaimsIdentity(user.ToClaimList(), CookieAuthenticationDefaults.AuthenticationScheme);
// Create authProperties
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
};
// Sign in
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
return Ok();
}
[AllowAnonymous]
[HttpPost("login")]
public async Task<IActionResult> Login([FromBody] Login login)
{
try
{
return dirSearchService.ValidateCredentials(login.Username, login.Password)
? await userService.ReadByUsernameAsync(login.Username).ThenAsync(
SuccessAsync: Login,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
logger.LogError("User could not be found, although verified by directory-search-service. It needs to be imported by UserManager. User name is {username}.", login.Username);
return StatusCode(StatusCodes.Status500InternalServerError);
})
: Unauthorized(localizer[WFKey.UserNotFoundOrWrongPassword].Value);
}
catch(Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[AllowAnonymous]
[HttpPost("login/{id}")]
public async Task<IActionResult> LoginById([FromRoute] int id, [FromQuery] string password)
{
try
{
return await userService.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async user
=> dirSearchService.ValidateCredentials(user.Username, password)
? await Login(user)
: Unauthorized(localizer[WFKey.WrongPassword].Value),
Fail: (msg, ntc) =>
{
if (ntc.HasFlag(Flag.NotFound))
return Unauthorized(Key.UserNotFound);
logger.LogNotice(ntc);
return StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[Authorize]
[HttpGet("user")]
public async Task<IActionResult> GetUserWithClaims()
{
try
{
// Extract the username from the Name claim.
string? username = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value;
if (string.IsNullOrEmpty(username))
return Unauthorized();
return await userService.ReadByUsernameAsync(username)
.ThenAsync(Ok, IActionResult (m, n) =>
{
logger.LogNotice(n);
return NotFound(Result.Fail().Message(localizer[Key.UserNotFound].Value));
});
}
catch (Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
[Authorize]
[HttpPost("logout")]
public async Task<IActionResult> Logout()
{
try
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
catch(Exception ex)
{
logger.LogError(ex, "{Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}
}

View File

@@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ConfigController(ILogger<ConfigController> logger, IConfigService service) : CRUDControllerBaseWithErrorHandling<IConfigService, ConfigCreateDto, ConfigDto, ConfigUpdateDto, Config, int>(logger, service)
public ConfigController(ILogger<ConfigController> logger, IConfigService service) : base(logger, service)
{
}
}

View File

@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using WorkFlow.API.Models;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers;
//TODO: implement up-to-date AuthController in UserManager
[APIKeyAuth]
[Route("api/Auth")]
[ApiController]
[Tags("Auth")]
public class PlaceholderAuthController : ControllerBase
{
[HttpPost]
public IActionResult CreateTokenViaBody([FromBody] Login login)
{
throw new NotImplementedException();
}
[HttpGet("check")]
[Authorize]
public IActionResult Check() => Ok();
}

View File

@@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileController(ILogger<ProfileController> logger, IProfileService service) : CRUDControllerBaseWithErrorHandling<IProfileService, ProfileCreateDto, ProfileDto, ProfileUpdateDto, Profile, int>(logger, service)
public ProfileController(ILogger<ProfileController> logger, IProfileService service) : base(logger, service)
{
}
}

View File

@@ -7,114 +7,120 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.ProfileControlsTF;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileControlsTFController : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : CRUDControllerBase<IProfileControlsTFService, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTFUpdateDto, ProfileControlsTF, int>(logger, service)
private readonly ILogger<ProfileControlsTFController> logger;
public ProfileControlsTFController(ILogger<ProfileControlsTFController> logger, IProfileControlsTFService service) : base(logger, service)
{
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto);
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = false,
int? profileId = null, int? objId = null, bool? profileActive = null)
[NonAction]
public override Task<IActionResult> Update(ProfileControlsTFUpdateDto updateDto) => base.Update(updateDto);
[HttpGet]
public async Task<IActionResult> GetAsync(
bool withProfile = true, bool withUser = false,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
try
{
try
if (!this.TryGetUserId(out int? id))
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
catch (Exception ex)
else if (id is null)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileControlsTFCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
return await _service.ReadAsync(
withProfile: withProfile, withUser: withUser,
userId: id,
profileId: profileId, objId: objId, profileActive: profileActive)
.ThenAsync(
Success: pctf => pctf.Any() ? Ok(pctf) : NotFound(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
logger.LogNotice(ntc);
return NotFound();
});
}
catch (Exception ex)
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpPost]
public override async Task<IActionResult> Create([FromBody] ProfileControlsTFCreateDto createDto)
{
try
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (id is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
if (createDto.UserId != id)
return Unauthorized();
return await base.Create(createDto);
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
[HttpDelete]
public override async Task<IActionResult> Delete([FromRoute] int id)
{
try
{
if (!this.TryGetUserId(out int? userId))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if (userId is null)
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
return await _service.ReadByIdAsync(id).ThenAsync(
SuccessAsync: async pctf => pctf.UserId == userId ? await base.Delete(id) : Unauthorized(),
Fail: IActionResult (msg, ntc) =>
{
_logger.LogNotice(ntc);
return ntc.HasFlag(Flag.NotFound) ? NotFound() : StatusCode(StatusCodes.Status500InternalServerError);
});
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -13,8 +13,15 @@ namespace WorkFlow.API.Controllers
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>(logger, service)
public class ProfileObjStateController : CRUDControllerBaseWithErrorHandling<IProfileObjStateService, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjStateUpdateDto, ProfileObjState, int>
{
private readonly ILogger<ProfileObjStateController> logger;
public ProfileObjStateController(ILogger<ProfileObjStateController> logger, IProfileObjStateService service) : base(logger, service)
{
this.logger = logger;
}
[NonAction]
public override Task<IActionResult> GetAll() => base.GetAll();

View File

@@ -6,13 +6,15 @@ using WorkFlow.Application.Contracts;
using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
namespace WorkFlow.API.Controllers
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class StateController(ILogger<StateController> logger, IStateService service) : CRUDControllerBaseWithErrorHandling<IStateService, StateCreateDto, StateDto, StateUpdateDto, State, int>(logger, service)
public StateController(ILogger<StateController> logger, IStateService service) : base(logger, service)
{
}
}

View File

@@ -4,43 +4,51 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using WorkFlow.API.Attributes;
namespace WorkFlow.API.Controllers
namespace WorkFlow.API.Controllers;
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController : ControllerBase
{
[APIKeyAuth]
[Route("api/[controller]")]
[ApiController]
[Authorize]
public class UserController(ILogger<UserController> logger, IUserService userService) : ControllerBase
private readonly ILogger<UserController> logger;
private readonly IUserService userService;
public UserController(ILogger<UserController> logger, IUserService userService)
{
[HttpGet]
public async Task<IActionResult> GetAsync()
this.logger = logger;
this.userService = userService;
}
[HttpGet]
public async Task<IActionResult> GetAsync()
{
try
{
try
if (!this.TryGetUserId(out int? id))
{
if (!this.TryGetUserId(out int? id))
{
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
else if(id is int id_int)
return await userService.ReadByIdAsync(id_int).ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
else
{
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
logger.LogError("Authorization failed: User ID claim not found.");
return StatusCode(StatusCodes.Status500InternalServerError, "Failed to retrieve user identity.");
}
catch (Exception ex)
else if(id is int id_int)
return await userService.ReadByIdAsync(id_int).ThenAsync(
Success: Ok,
Fail: IActionResult (msg, ntc) =>
{
logger.LogNotice(ntc);
return NotFound();
});
else
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
logger.LogError("Invalid user ID: Retrieved ID is null or not an integer.");
return StatusCode(StatusCodes.Status500InternalServerError, "Invalid user ID.");
}
}
catch (Exception ex)
{
logger.LogError(ex, "An unexpected error occurred while processing the request: {Message}", ex.Message);
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred.");
}
}
}

View File

@@ -4,33 +4,39 @@ using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using WorkFlow.API.Models;
namespace WorkFlow.API.Filters
namespace WorkFlow.API.Filters;
public class APIKeyAuthHeaderOpFilter : IOperationFilter
{
public class APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> options, IWebHostEnvironment environment) : IOperationFilter
private readonly APIKeyAuthOptions apiKeyAuthOptions;
private readonly IWebHostEnvironment environment;
public APIKeyAuthHeaderOpFilter(IOptions<APIKeyAuthOptions> options, IWebHostEnvironment environment)
{
private readonly APIKeyAuthOptions apiKeyAuthOptions = options.Value;
this.environment = environment;
apiKeyAuthOptions = options.Value;
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var param = new OpenApiParameter
{
var param = new OpenApiParameter
Name = apiKeyAuthOptions.HeaderName,
In = ParameterLocation.Header,
Required = true,
AllowEmptyValue = false,
Schema = new OpenApiSchema
{
Name = apiKeyAuthOptions.HeaderName,
In = ParameterLocation.Header,
Required = true,
AllowEmptyValue = false,
Schema = new OpenApiSchema
{
Type = "string"
}
};
Type = "string"
}
};
if(environment.IsDevelopment())
param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key);
if(environment.IsDevelopment())
param.Schema.Default = new OpenApiString(apiKeyAuthOptions.Key);
if (apiKeyAuthOptions.SwaggerDescription is not null)
param.Description = apiKeyAuthOptions.SwaggerDescription;
if (apiKeyAuthOptions.SwaggerDescription is not null)
param.Description = apiKeyAuthOptions.SwaggerDescription;
operation.Parameters.Add(param);
}
operation.Parameters.Add(param);
}
}

View File

@@ -1,14 +1,22 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc;
namespace WorkFlow.API.Filters
namespace WorkFlow.API.Filters;
public class APIKeyAuthFilter : IAuthorizationFilter
{
public class APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key") : IAuthorizationFilter
private readonly Func<string?, bool> isValidKey;
private readonly string headerName;
public APIKeyAuthFilter(Func<string?, bool> isValidKey, string headerName = "X-API-Key")
{
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!isValidKey(context.HttpContext.Request.Headers[headerName]))
context.Result = new UnauthorizedResult();
}
this.isValidKey = isValidKey;
this.headerName = headerName;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
if (!isValidKey(context.HttpContext.Request.Headers[headerName]))
context.Result = new UnauthorizedResult();
}
}

10
WorkFlow.API/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,10 @@
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'dotnet build'
}
}
}
}

View File

@@ -0,0 +1,18 @@
namespace WorkFlow.API;
public class LazyServiceProvider : IServiceProvider
{
private Lazy<IServiceProvider>? _serviceProvider;
public Func<IServiceProvider> Factory
{
set => _serviceProvider = new(value);
}
public object? GetService(Type serviceType)
{
if (_serviceProvider is null)
throw new InvalidOperationException("GetService cannot be called before _serviceProvider is set.");
return _serviceProvider.Value.GetService(serviceType);
}
}

View File

@@ -0,0 +1,12 @@
namespace WorkFlow.API.Models;
public class AuthTokenKeys
{
public string Cookie { get; init; } = "AuthToken";
public string QueryString { get; init; } = "AuthToken";
public string Issuer { get; init; } = "auth.digitaldata.works";
public string Audience { get; init; } = "work-flow.digitaldata.works";
}

View File

@@ -1,4 +1,4 @@
namespace WorkFlow.API.Models
{
public record Login(string Username, string Password);
public record Login(int? UserId, string? Username, string Password);
}

View File

@@ -5,13 +5,14 @@ namespace WorkFlow.API.Models
{
public static class ModelExtensions
{
public static List<Claim> ToClaimList(this UserReadDto user) => [
public static List<Claim> ToClaimList(this UserReadDto user) => new()
{
new (ClaimTypes.NameIdentifier, user.Id.ToString()),
new (ClaimTypes.Name, user.Username),
new (ClaimTypes.Surname, user.Name ?? ""),
new (ClaimTypes.GivenName, user.Prename ?? ""),
new (ClaimTypes.Email, user.Email ?? "")
];
};
public static Dictionary<string, object> ToClaimDictionary(this UserReadDto user) => user.ToClaimList().ToDictionary(claim => claim.Type, claim => (object) claim.Value);
}

View File

@@ -2,7 +2,6 @@ using WorkFlow.Application;
using DigitalData.UserManager.Application;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Infrastructure;
using Microsoft.AspNetCore.Authentication.Cookies;
using DigitalData.Core.API;
using DigitalData.Core.Application;
using DigitalData.UserManager.Application.DTOs.User;
@@ -14,6 +13,10 @@ using WorkFlow.API.Extensions;
using WorkFlow.API.Filters;
using Microsoft.OpenApi.Models;
using DigitalData.Auth.Client;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using WorkFlow.API;
using Microsoft.Extensions.Options;
using DigitalData.Core.Abstractions.Security;
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
logger.Info("Logging initialized.");
@@ -47,33 +50,80 @@ try
else
throw new("The API Key Authorization configuration is not available in the app settings, even though the app is not in development or DiP mode and API Key Authorization is not disabled.");
// Created separately from AuthClientParams (added via options) for use in Jwt Bearer configuration
var authPublicKey = config.GetSection("AuthPublicKey").Get<ClientPublicKey>() ?? throw new InvalidOperationException("The AuthPublicKey configuration is missing or invalid.");
var lazyProvider = new LazyServiceProvider();
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"), opt => opt.PublicKeys.Add(authPublicKey));
builder.Services.AddAuthHubClient(config.GetSection("AuthClientParams"));
builder.Services.AddControllers();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer(opt =>
var authTokenKeys = config.GetSection(nameof(AuthTokenKeys)).Get<AuthTokenKeys>() ?? new();
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(opt =>
{
opt.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
{
return [authPublicKey.SecurityKey];
var clientParams = lazyProvider.GetRequiredService<IOptions<ClientParams>>()?.Value;
var publicKey = clientParams!.PublicKeys.Get(authTokenKeys.Issuer, authTokenKeys.Audience);
return new List<SecurityKey>() { publicKey.SecurityKey };
},
ValidateIssuer = true,
ValidIssuer = authPublicKey.Issuer,
ValidIssuer = authTokenKeys.Issuer,
ValidateAudience = true,
ValidAudience = authPublicKey.Audience
ValidAudience = authTokenKeys.Audience,
};
opt.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
// if there is no token read related cookie or query string
if (context.Token is null) // if there is no token
{
if (context.Request.Cookies.TryGetValue(authTokenKeys.Cookie, out var cookieToken) && cookieToken is not null)
context.Token = cookieToken;
else if (context.Request.Query.TryGetValue(authTokenKeys.QueryString, out var queryStrToken))
context.Token = queryStrToken;
}
return Task.CompletedTask;
}
};
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(setupAct =>
{
setupAct.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
setupAct.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
if (!disableAPIKeyAuth)
setupAct.OperationFilter<APIKeyAuthHeaderOpFilter>();
@@ -83,6 +133,8 @@ try
var app = builder.Build();
lazyProvider.Factory = () => app.Services;
// Configure the HTTP request pipeline.
if (app.IsDevOrDiP() && app.Configuration.GetValue<bool>("EnableSwagger"))
{

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net7\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
<PropertyGroup>
<WebPublishMethod>Package</WebPublishMethod>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<ExcludeApp_Data>false</ExcludeApp_Data>
<ProjectGuid>4fb33592-ef0d-47c3-9cde-03b2ef12be00</ProjectGuid>
<DesktopBuildPackageLocation>P:\Install .Net\0 DD - Smart UP\workFLOW\API\net8\$(Version)\workFLOW.API.zip</DesktopBuildPackageLocation>
<PackageAsSingleFile>true</PackageAsSingleFile>
<DeployIisAppPath>WorkFlow.API</DeployIisAppPath>
<_TargetId>IISWebDeployPackage</_TargetId>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@@ -12,7 +12,7 @@
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5130",
"environmentVariables": {
@@ -22,7 +22,7 @@
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7120;http://localhost:5130",
"environmentVariables": {
@@ -31,7 +31,7 @@
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchBrowser": false,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"

View File

@@ -1,20 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>1.0.0.0</PackageId>
<Version>1.0.0.0</Version>
<PackageId>WorkFlow.API</PackageId>
<Version>1.1.0</Version>
<Company>Digital Data GmbH</Company>
<Product>WorkFlow.API</Product>
<Title>WorkFlow.API</Title>
<AssemblyVersion>1.1.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.1.5" />
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfile - Copy.pubxml" />
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet7.pubxml" />
<_WebToolingArtifacts Remove="Properties\PublishProfiles\IISProfileNet8.pubxml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="DigitalData.Auth.Client" Version="1.3.3" />
<PackageReference Include="DigitalData.Core.API" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.20" />
<PackageReference Include="NLog" Version="5.3.4" />
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.14" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />

View File

@@ -78,11 +78,14 @@
}
},
"AuthClientParams": {
"Url": "https://localhost:7192",
"PublicKeys": []
},
"AuthPublicKey": {
"Issuer": "auth.digitaldata.works",
"Audience": "work-flow.digitaldata.works"
"Url": "https://localhost:7192/auth-hub",
"PublicKeys": [
{
"Issuer": "auth.digitaldata.works",
"Audience": "work-flow.digitaldata.works",
"Content": "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3QCd7dH/xOUITFZbitMa/xnh8a0LyL6ZBvSRAwkI9ceplTRSHJXoM1oB+xtjWE1kOuHVLe941Tm03szS4+/rHIm0Ejva/KKlv7sPFAHE/pWuoPS303vOHgI4HAFcuwywA8CghUWzaaK5LU/Hl8srWwxBHv5hKIUjJFJygeAIENvFOZ1gFbB3MPEC99PiPOwAmfl4tMQUmSsFyspl/RWVi7bTv26ZE+m3KPcWppmvmYjXlSitxRaySxnfFvpca/qWfd/uUUg2KWKtpAwWVkqr0qD9v3TyKSgHoGDsrFpwSx8qufUJSinmZ1u/0iKl6TXeHubYS4C4SUSVjOWXymI2ZQIDAQAB-----END PUBLIC KEY-----"
}
],
"RetryDelay": "00:00:05"
}
}

View File

@@ -6,11 +6,12 @@ using WorkFlow.Application.DTO.Config;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
namespace WorkFlow.Application.Services;
public class ConfigService : CRUDService<IConfigRepository, ConfigCreateDto, ConfigDto, Config, int>,
IConfigService, ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
{
public class ConfigService(IConfigRepository repository, IMapper mapper)
: CRUDService<IConfigRepository, ConfigCreateDto, ConfigDto, Config, int>(repository, mapper),
IConfigService, ICRUDService<ConfigCreateDto, ConfigDto, Config, int>
public ConfigService(IConfigRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -9,10 +9,13 @@ using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
{
public class ProfileControlsTFService(IProfileControlsTFRepository repository, IMapper mapper)
: CRUDService<IProfileControlsTFRepository, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>(repository, mapper),
public class ProfileControlsTFService : CRUDService<IProfileControlsTFRepository, ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>,
IProfileControlsTFService, ICRUDService<ProfileControlsTFCreateDto, ProfileControlsTFDto, ProfileControlsTF, int>
{
public ProfileControlsTFService(IProfileControlsTFRepository repository, IMapper mapper) : base(repository, mapper)
{
}
public async Task<DataResult<IEnumerable<ProfileControlsTFDto>>> ReadAsync(
bool withProfile = true, bool withUser = false,
int? userId = null, string? username = null,

View File

@@ -7,26 +7,28 @@ using WorkFlow.Application.DTO.ProfileObjState;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
namespace WorkFlow.Application.Services;
public class ProfileObjStateService : CRUDService<IProfileObjStateRepository, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>,
IProfileObjStateService, ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
{
public class ProfileObjStateService(IProfileObjStateRepository repository, IMapper mapper)
: CRUDService<IProfileObjStateRepository, ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>(repository, mapper),
IProfileObjStateService, ICRUDService<ProfileObjStateCreateDto, ProfileObjStateDto, ProfileObjState, int>
public ProfileObjStateService(IProfileObjStateRepository repository, IMapper mapper) : base(repository, mapper)
{
public async Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pos_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
}
var post_dto_list = _mapper.Map<IEnumerable<ProfileObjStateDto>>(pos_list);
public async Task<DataResult<IEnumerable<ProfileObjStateDto>>> ReadAsync(
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
{
var pos_list = await _repository.ReadAsync(
isReadonly: true,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive);
return Result.Success(post_dto_list);
}
var post_dto_list = _mapper.Map<IEnumerable<ProfileObjStateDto>>(pos_list);
return Result.Success(post_dto_list);
}
}

View File

@@ -5,11 +5,12 @@ using WorkFlow.Application.DTO.Profile;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
namespace WorkFlow.Application.Services;
public class ProfileService : CRUDService<IProfileRepository, ProfileCreateDto, ProfileDto, Profile, int>,
IProfileService, ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
{
public class ProfileService(IProfileRepository repository, AutoMapper.IMapper mapper)
: CRUDService<IProfileRepository, ProfileCreateDto, ProfileDto, Profile, int>(repository, mapper),
IProfileService, ICRUDService<ProfileCreateDto, ProfileDto, Profile, int>
public ProfileService(IProfileRepository repository, AutoMapper.IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -6,11 +6,12 @@ using WorkFlow.Application.DTO.State;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Application.Services
namespace WorkFlow.Application.Services;
public class StateService : CRUDService<IStateRepository, StateCreateDto, StateDto, State, int>,
IStateService, ICRUDService<StateCreateDto, StateDto, State, int>
{
public class StateService(IStateRepository repository, IMapper mapper)
: CRUDService<IStateRepository, StateCreateDto, StateDto, State, int>(repository, mapper),
IStateService, ICRUDService<StateCreateDto, StateDto, State, int>
public StateService(IStateRepository repository, IMapper mapper) : base(repository, mapper)
{
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@@ -6,7 +6,10 @@ using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
//TODO: Make the db context type generic so that it can be used by other projects with different db contexts.
public class ConfigRepository(WFDBContext dbContext) : CRUDRepository<Config, int, WFDBContext>(dbContext, dbContext.Configs), IConfigRepository, ICRUDRepository<Config, int>
public class ConfigRepository : CRUDRepository<Config, int, WFDBContext>, IConfigRepository, ICRUDRepository<Config, int>
{
public ConfigRepository(WFDBContext dbContext) : base(dbContext, dbContext.Configs)
{
}
}
}

View File

@@ -6,8 +6,12 @@ using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
{
public class ProfileControlsTFRepository(WFDBContext dbContext) : CRUDRepository<ProfileControlsTF, int, WFDBContext>(dbContext, dbContext.ProfileControlsTFs), IProfileControlsTFRepository, ICRUDRepository<ProfileControlsTF, int>
public class ProfileControlsTFRepository : CRUDRepository<ProfileControlsTF, int, WFDBContext>, IProfileControlsTFRepository, ICRUDRepository<ProfileControlsTF, int>
{
public ProfileControlsTFRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileControlsTFs)
{
}
protected override IQueryable<ProfileControlsTF> ReadOnly() => base.ReadOnly().Include(pctf => pctf.Profile).Include(pctf => pctf.User);
protected IQueryable<ProfileControlsTF> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, int? profileId = null, int? userId = null, string? username = null, int? objId = null, bool? profileActive = null)

View File

@@ -4,56 +4,59 @@ using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileObjStateRepository : CRUDRepository<ProfileObjState, int, WFDBContext>, IProfileObjStateRepository, ICRUDRepository<ProfileObjState, int>
{
public class ProfileObjStateRepository(WFDBContext dbContext) : CRUDRepository<ProfileObjState, int, WFDBContext>(dbContext, dbContext.ProfileObjStates), IProfileObjStateRepository, ICRUDRepository<ProfileObjState, int>
public ProfileObjStateRepository(WFDBContext dbContext) : base(dbContext, dbContext.ProfileObjStates)
{
protected override IQueryable<ProfileObjState> ReadOnly() => base.ReadOnly().Include(pos => pos.Profile).Include(pos => pos.State);
protected IQueryable<ProfileObjState> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, bool withState = true, int? profileId = null, int? userId = null, string? username = null, int? stateId = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (withState)
query = query.Include(pctf => pctf.State);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (stateId is null)
query = query.Where(pctf => pctf.State!.Id == stateId);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}
protected override IQueryable<ProfileObjState> ReadOnly() => base.ReadOnly().Include(pos => pos.Profile).Include(pos => pos.State);
protected IQueryable<ProfileObjState> Read(bool isReadonly = false, bool withProfile = true, bool withUser = true, bool withState = true, int? profileId = null, int? userId = null, string? username = null, int? stateId = null, int? objId = null, bool? profileActive = null)
{
var query = isReadonly ? _dbSet.AsNoTracking() : _dbSet.AsQueryable();
if (withProfile)
query = query.Include(pctf => pctf.Profile);
if (withUser)
query = query.Include(pctf => pctf.User);
if (withState)
query = query.Include(pctf => pctf.State);
if (profileId is not null)
query = query.Where(pctf => pctf.ProfileId == profileId);
if (userId is not null)
query = query.Where(pctf => pctf.UserId == userId);
if (username is null)
query = query.Where(pctf => pctf.User!.Username == username);
if (stateId is null)
query = query.Where(pctf => pctf.State!.Id == stateId);
if (objId is not null)
query = query.Where(pctf => pctf.ObjId == objId);
if (profileActive is not null)
query = query.Where(pctf => pctf.Profile!.Active == profileActive);
return query;
}
public async Task<IEnumerable<ProfileObjState>> ReadAsync(
bool isReadonly = true,
bool withProfile = true, bool withUser = true, bool withState = true,
int? userId = null, string? username = null,
int? profileId = null, int? objId = null, bool? profileActive = null)
=> await Read(
isReadonly: isReadonly,
withProfile: withProfile, withUser: withUser, withState: withState,
userId: userId, username: username,
profileId: profileId, objId: objId, profileActive: profileActive)
.ToListAsync();
}

View File

@@ -3,9 +3,11 @@ using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
namespace WorkFlow.Infrastructure.Repositories;
public class ProfileRepository : CRUDRepository<Profile, int, WFDBContext>, IProfileRepository, ICRUDRepository<Profile, int>
{
public class ProfileRepository(WFDBContext dbContext) : CRUDRepository<Profile, int, WFDBContext>(dbContext, dbContext.Profiles), IProfileRepository, ICRUDRepository<Profile, int>
public ProfileRepository(WFDBContext dbContext) : base(dbContext, dbContext.Profiles)
{
}
}

View File

@@ -2,9 +2,12 @@
using DigitalData.Core.Infrastructure;
using WorkFlow.Domain.Entities;
using WorkFlow.Infrastructure.Contracts;
namespace WorkFlow.Infrastructure.Repositories
namespace WorkFlow.Infrastructure.Repositories;
public class StateRepository : CRUDRepository<State, int, WFDBContext>, IStateRepository, ICRUDRepository<State, int>
{
public class StateRepository(WFDBContext dbContext) : CRUDRepository<State, int, WFDBContext>(dbContext, dbContext.States), IStateRepository, ICRUDRepository<State, int>
public StateRepository(WFDBContext dbContext) : base(dbContext, dbContext.States)
{
}
}

View File

@@ -4,40 +4,43 @@ using DigitalData.UserManager.Infrastructure.Contracts;
using Microsoft.EntityFrameworkCore;
using WorkFlow.Domain.Entities;
namespace WorkFlow.Infrastructure
namespace WorkFlow.Infrastructure;
public class WFDBContext : DbContext, IUserManagerDbContext
{
public class WFDBContext(DbContextOptions options) : DbContext(options), IUserManagerDbContext
public DbSet<Config> Configs { get; set; }
public DbSet<ProfileControlsTF> ProfileControlsTFs { get; set; }
public DbSet<Profile> Profiles { get; set; }
public DbSet<ProfileObjState> ProfileObjStates { get; set; }
public DbSet<State> States { get; set; }
public DbSet<GroupOfUser> GroupOfUsers { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<ModuleOfUser> ModuleOfUsers { get; set; }
public DbSet<Module> Modules { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserRep> UserReps { get; set; }
public DbSet<ClientUser> ClientUsers { get; set; }
public WFDBContext(DbContextOptions options) : base(options)
{
public DbSet<Config> Configs { get; set; }
}
public DbSet<ProfileControlsTF> ProfileControlsTFs { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//configure model builder for user manager tables
modelBuilder.ConfigureUserManager();
public DbSet<Profile> Profiles { get; set; }
public DbSet<ProfileObjState> ProfileObjStates { get; set; }
public DbSet<State> States { get; set; }
public DbSet<GroupOfUser> GroupOfUsers { get; set; }
public DbSet<Group> Groups { get; set; }
public DbSet<ModuleOfUser> ModuleOfUsers { get; set; }
public DbSet<Module> Modules { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserRep> UserReps { get; set; }
public DbSet<ClientUser> ClientUsers { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//configure model builder for user manager tables
modelBuilder.ConfigureUserManager();
base.OnModelCreating(modelBuilder);
}
base.OnModelCreating(modelBuilder);
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>