327 lines
14 KiB
C#
327 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Oqtane.Enums;
|
|
using Oqtane.Infrastructure;
|
|
using Oqtane.Models;
|
|
using Oqtane.Security;
|
|
using Oqtane.Shared;
|
|
using SZUAbsolventenverein.Module.PremiumArea.Models;
|
|
using SZUAbsolventenverein.Module.PremiumArea.Repository;
|
|
|
|
namespace SZUAbsolventenverein.Module.PremiumArea.Services
|
|
{
|
|
public class ServerEngineerApplicationService : IEngineerApplicationService
|
|
{
|
|
private readonly IEngineerApplicationRepository _repository;
|
|
private readonly IPremiumService _premiumService;
|
|
private readonly IUserPermissions _userPermissions;
|
|
private readonly ILogManager _logger;
|
|
private readonly IHttpContextAccessor _accessor;
|
|
private readonly Alias _alias;
|
|
|
|
public ServerEngineerApplicationService(IEngineerApplicationRepository repository,
|
|
IPremiumService premiumService, IUserPermissions userPermissions, ITenantManager tenantManager,
|
|
ILogManager logger, IHttpContextAccessor accessor)
|
|
{
|
|
_repository = repository;
|
|
_premiumService = premiumService;
|
|
_userPermissions = userPermissions;
|
|
_logger = logger;
|
|
_accessor = accessor;
|
|
_alias = tenantManager.GetAlias();
|
|
}
|
|
|
|
public Task<List<EngineerApplication>> GetApplicationsAsync(int ModuleId)
|
|
{
|
|
var user = _accessor.HttpContext.User;
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId,
|
|
PermissionNames.Edit)) // Admin/Edit
|
|
{
|
|
return Task.FromResult(_repository.GetEngineerApplications(ModuleId).ToList());
|
|
}
|
|
|
|
var userId = _accessor.HttpContext.GetUserId();
|
|
var results = new List<EngineerApplication>();
|
|
|
|
// Always include the user's own applications (needed for Apply.razor)
|
|
if (userId != -1)
|
|
{
|
|
var ownApps = _repository.GetEngineerApplications(ModuleId)
|
|
.Where(a => a.UserId == userId).ToList();
|
|
results.AddRange(ownApps);
|
|
}
|
|
|
|
// Check if Premium - also show approved/published apps from others
|
|
if (IsUserPremium(user))
|
|
{
|
|
var approved = _repository.GetEngineerApplications(ModuleId, "Approved");
|
|
var published = _repository.GetEngineerApplications(ModuleId, "Published");
|
|
results.AddRange(approved.Union(published));
|
|
// Remove duplicates (own apps might already be in approved/published)
|
|
results = results.GroupBy(a => a.ApplicationId).Select(g => g.First()).ToList();
|
|
}
|
|
|
|
return Task.FromResult(results);
|
|
}
|
|
|
|
public Task<List<EngineerApplication>> GetApplicationsAsync(int ModuleId, string status)
|
|
{
|
|
var user = _accessor.HttpContext.User;
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
|
|
{
|
|
return Task.FromResult(_repository.GetEngineerApplications(ModuleId, status).ToList());
|
|
}
|
|
|
|
if ((status == "Approved" || status == "Published") && IsUserPremium(user))
|
|
{
|
|
return Task.FromResult(_repository.GetEngineerApplications(ModuleId, status).ToList());
|
|
}
|
|
|
|
return Task.FromResult(new List<EngineerApplication>());
|
|
}
|
|
|
|
public Task<EngineerApplication> GetApplicationAsync(int ApplicationId, int ModuleId)
|
|
{
|
|
var app = _repository.GetEngineerApplication(ApplicationId);
|
|
if (app == null || app.ModuleId != ModuleId) return Task.FromResult<EngineerApplication>(null);
|
|
|
|
var user = _accessor.HttpContext.User;
|
|
var userId = _accessor.HttpContext.GetUserId();
|
|
|
|
// Allow if Admin OR Owner OR (Premium AND (Approved OR Published))
|
|
bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId,
|
|
PermissionNames.Edit);
|
|
bool isOwner = (userId != -1 && app.UserId == userId);
|
|
bool isPremiumViewer = ((app.Status == "Approved" || app.Status == "Published") && IsUserPremium(user));
|
|
|
|
if (isAdmin || isOwner || isPremiumViewer)
|
|
{
|
|
return Task.FromResult(app);
|
|
}
|
|
|
|
return Task.FromResult<EngineerApplication>(null);
|
|
}
|
|
|
|
public Task<EngineerApplication> AddApplicationAsync(EngineerApplication Application)
|
|
{
|
|
var user = _accessor.HttpContext.User;
|
|
var userId = _accessor.HttpContext.GetUserId();
|
|
|
|
if (userId == -1) // Not logged in
|
|
{
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Add Attempt (Anonymous)");
|
|
return Task.FromResult<EngineerApplication>(null);
|
|
}
|
|
|
|
// Check if allowed to view module (Registered Users usually can View)
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId,
|
|
PermissionNames.View))
|
|
{
|
|
Application.UserId = userId;
|
|
// Auto-publish if file uploaded (Checked by Status=Published from client)
|
|
// If client sends "Published", we accept it.
|
|
if (string.IsNullOrEmpty(Application.Status)) Application.Status = "Draft";
|
|
|
|
// Set ApprovedOn if Published? user asked for removal of admin approval.
|
|
if (Application.Status == "Published")
|
|
{
|
|
Application.SubmittedOn = DateTime.UtcNow;
|
|
Application.ApprovedOn = DateTime.UtcNow; // Effectively approved.
|
|
}
|
|
|
|
Application = _repository.AddEngineerApplication(Application);
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Application Added {Application}",
|
|
Application);
|
|
return Task.FromResult(Application);
|
|
}
|
|
|
|
return Task.FromResult<EngineerApplication>(null);
|
|
}
|
|
|
|
public Task<EngineerApplication> UpdateApplicationAsync(EngineerApplication Application)
|
|
{
|
|
var existing = _repository.GetEngineerApplication(Application.ApplicationId);
|
|
if (existing == null) return Task.FromResult<EngineerApplication>(null);
|
|
|
|
var user = _accessor.HttpContext.User;
|
|
var userId = _accessor.HttpContext.GetUserId();
|
|
bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, Application.ModuleId,
|
|
PermissionNames.Edit);
|
|
bool isOwner = (userId != -1 && existing.UserId == userId);
|
|
|
|
if (isAdmin || isOwner)
|
|
{
|
|
if (!isAdmin)
|
|
{
|
|
// Owner can update their own application
|
|
// Accept "Published" status from client (auto-publish without admin approval)
|
|
if (Application.Status == "Published")
|
|
{
|
|
Application.SubmittedOn ??= DateTime.UtcNow;
|
|
Application.ApprovedOn ??= DateTime.UtcNow;
|
|
}
|
|
else
|
|
{
|
|
// Keep existing status if client didn't explicitly set to Published
|
|
Application.Status = existing.Status;
|
|
}
|
|
}
|
|
|
|
Application = _repository.UpdateEngineerApplication(Application);
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Updated {Application}",
|
|
Application);
|
|
return Task.FromResult(Application);
|
|
}
|
|
|
|
_logger.Log(LogLevel.Error, this, LogFunction.Security,
|
|
"Unauthorized Update Attempt. UserId: {UserId}, AppOwner: {OwnerId}, IsAdmin: {IsAdmin}",
|
|
userId, existing.UserId, isAdmin);
|
|
return Task.FromResult<EngineerApplication>(null);
|
|
}
|
|
|
|
public Task DeleteApplicationAsync(int ApplicationId, int ModuleId)
|
|
{
|
|
var existing = _repository.GetEngineerApplication(ApplicationId);
|
|
var user = _accessor.HttpContext.User;
|
|
var userId = _accessor.HttpContext.GetUserId();
|
|
bool isAdmin = _userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId,
|
|
PermissionNames.Edit);
|
|
|
|
if (existing != null && (isAdmin || existing.UserId == userId))
|
|
{
|
|
_repository.DeleteEngineerApplication(ApplicationId);
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Application Deleted {Id}", ApplicationId);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
// Custom Methods not just CRUD
|
|
public Task ApproveApplicationAsync(int ApplicationId, int ModuleId)
|
|
{
|
|
var user = _accessor.HttpContext.User;
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
|
|
{
|
|
var app = _repository.GetEngineerApplication(ApplicationId);
|
|
if (app != null)
|
|
{
|
|
app.Status = "Approved"; // Keep Approved status for Admin explicitly approving (locking)
|
|
app.ApprovedOn = DateTime.UtcNow;
|
|
// app.ReportReason = null; // Optional: keep history?
|
|
// app.ReportCount = 0;
|
|
|
|
_repository.UpdateEngineerApplication(app);
|
|
|
|
// Grant Premium
|
|
_premiumService.GrantPremium(app.UserId, 12, "engineer_application", $"AppId:{app.ApplicationId}");
|
|
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Approved {Id}",
|
|
ApplicationId);
|
|
}
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task RejectApplicationAsync(int ApplicationId, int ModuleId, string Reason)
|
|
{
|
|
var user = _accessor.HttpContext.User;
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
|
|
{
|
|
var app = _repository.GetEngineerApplication(ApplicationId);
|
|
if (app != null)
|
|
{
|
|
app.Status = "Rejected";
|
|
_repository.UpdateEngineerApplication(app);
|
|
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Rejected {Id}",
|
|
ApplicationId);
|
|
}
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task ReportApplicationAsync(int ApplicationId, int ModuleId, string Reason)
|
|
{
|
|
// Allow any View authorized user to report?
|
|
// Or only Premium users?
|
|
// Users who can VIEW the application can report it.
|
|
// Since we restrict View to Premium (or Owner/Admin), we check that.
|
|
|
|
// First, get the application to check existence
|
|
var app = _repository.GetEngineerApplication(ApplicationId);
|
|
if (app == null || app.ModuleId != ModuleId) return Task.CompletedTask;
|
|
|
|
var user = _accessor.HttpContext.User;
|
|
|
|
// Check if user is allowed to View this app
|
|
bool canView = false;
|
|
|
|
// Admin/Edit
|
|
if (_userPermissions.IsAuthorized(user, _alias.SiteId, EntityNames.Module, ModuleId, PermissionNames.Edit))
|
|
canView = true;
|
|
|
|
// Premium
|
|
else if (IsUserPremium(user) && (app.Status == "Approved" || app.Status == "Published")) canView = true;
|
|
|
|
if (canView)
|
|
{
|
|
_repository.UpdateEngineerApplication(app);
|
|
// Send Notification?
|
|
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Application Reported {Id}", ApplicationId);
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private bool IsUserPremium(System.Security.Claims.ClaimsPrincipal user)
|
|
{
|
|
if (!user.Identity.IsAuthenticated)
|
|
return false;
|
|
|
|
// Check 1: Oqtane role "Premium Member" (matches the UI-level check in ApplicationList.razor)
|
|
if (user.IsInRole("Premium Member"))
|
|
return true;
|
|
|
|
// Check 2: Custom UserPremium DB table (for premium granted via GrantPremium/payment)
|
|
int userId = -1;
|
|
var claim = user.Claims.FirstOrDefault(item =>
|
|
item.Type == System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (claim != null)
|
|
{
|
|
int.TryParse(claim.Value, out userId);
|
|
}
|
|
|
|
if (userId != -1)
|
|
{
|
|
return _premiumService.IsPremium(userId);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Quick helper for GetUserId if not present in Usings
|
|
public static class ClaimsPrincipalExtensions
|
|
{
|
|
public static int GetUserId(this HttpContext context)
|
|
{
|
|
if (context?.User?.Identity?.IsAuthenticated == true)
|
|
{
|
|
var claim = context.User.Claims.FirstOrDefault(item =>
|
|
item.Type == System.Security.Claims.ClaimTypes.NameIdentifier);
|
|
if (claim != null && int.TryParse(claim.Value, out int userId))
|
|
{
|
|
return userId;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
}
|
|
}
|