using System.Collections.Generic; using Microsoft.AspNetCore.Mvc; using Oqtane.Models; using Oqtane.Shared; using Microsoft.AspNetCore.Authorization; using System.IO; using System.Reflection; using System.Linq; using Microsoft.AspNetCore.Hosting; using Oqtane.Enums; using Oqtane.Infrastructure; using Oqtane.Repository; using Oqtane.Security; using System; using Microsoft.Extensions.DependencyInjection; using System.Text.Json; using System.Net; using Oqtane.Modules; namespace Oqtane.Controllers { [Route(ControllerRoutes.ApiRoute)] public class ModuleDefinitionController : Controller { private readonly IModuleDefinitionRepository _moduleDefinitions; private readonly IModuleRepository _modules; private readonly IPageModuleRepository _pagemodules; private readonly IPermissionRepository _permissions; private readonly ITenantRepository _tenants; private readonly ISqlRepository _sql; private readonly IUserPermissions _userPermissions; private readonly IInstallationManager _installationManager; private readonly IWebHostEnvironment _environment; private readonly IServiceProvider _serviceProvider; private readonly ITenantManager _tenantManager; private readonly ISyncManager _syncManager; private readonly ILogManager _logger; private readonly Alias _alias; public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, IModuleRepository module, IPageModuleRepository pageModule, IPermissionRepository permission, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger) { _moduleDefinitions = moduleDefinitions; _modules = module; _pagemodules = pageModule; _permissions = permission; _tenants = tenants; _sql = sql; _userPermissions = userPermissions; _installationManager = installationManager; _environment = environment; _serviceProvider = serviceProvider; _tenantManager = tenantManager; _syncManager = syncManager; _logger = logger; _alias = tenantManager.GetAlias(); } // GET: api/?siteid=x [HttpGet] public IEnumerable Get(string siteid) { int SiteId; if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) { List moduledefinitions = new List(); foreach (ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(SiteId)) { if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList)) { if (string.IsNullOrEmpty(moduledefinition.Version)) moduledefinition.Version = new Version(1, 0, 0).ToString(); moduledefinitions.Add(moduledefinition); } } return moduledefinitions; } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {SiteId}", siteid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // GET api//5?siteid=x [HttpGet("{id}")] public ModuleDefinition Get(int id, string siteid) { int SiteId; if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId) { ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId); if (moduledefinition != null && _userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.PermissionList)) { moduledefinition.Version = (string.IsNullOrEmpty(moduledefinition.Version)) ? new Version(1, 0, 0).ToString() : moduledefinition.Version; return moduledefinition; } else { if (moduledefinition != null) { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } else { HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; } return null; } } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; return null; } } // POST api/ [HttpPost] [Authorize(Roles = RoleNames.Host)] public ModuleDefinition Post([FromBody] ModuleDefinition moduleDefinition) { if (ModelState.IsValid) { string rootPath; DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath); string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template, Path.DirectorySeparatorChar.ToString()); if (!string.IsNullOrEmpty(moduleDefinition.ModuleDefinitionName)) { moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName.Replace("[Owner]", moduleDefinition.Owner).Replace("[Module]", moduleDefinition.Name); } else { moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + ".Module." + moduleDefinition.Name; } if (moduleDefinition.Template.ToLower().Contains("internal")) { rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString()); moduleDefinition.ServerManagerType = moduleDefinition.ModuleDefinitionName + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server"; moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName + ", Oqtane.Client"; } else { rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, moduleDefinition.Owner + ".Module." + moduleDefinition.Name, Path.DirectorySeparatorChar.ToString()); moduleDefinition.ServerManagerType = moduleDefinition.ModuleDefinitionName + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.ModuleDefinitionName + ".Server.Oqtane"; moduleDefinition.ModuleDefinitionName = moduleDefinition.ModuleDefinitionName + ", " + moduleDefinition.ModuleDefinitionName + ".Client.Oqtane"; } ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition); _logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Definition Created {ModuleDefinition}", moduleDefinition); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Post Attempt {ModuleDefinition}", moduleDefinition); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; moduleDefinition = null; } return moduleDefinition; } // PUT api//5 [HttpPut("{id}")] [Authorize(Roles = RoleNames.Admin)] public void Put(int id, [FromBody] ModuleDefinition moduleDefinition) { if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && moduleDefinition.ModuleDefinitionId == id && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null) { _moduleDefinitions.UpdateModuleDefinition(moduleDefinition); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduleDefinition.ModuleDefinitionId, SyncEventActions.Update); _logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Put Attempt {ModuleDefinition}", moduleDefinition); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } // DELETE api//5?siteid=x [HttpDelete("{id}")] [Authorize(Roles = RoleNames.Host)] public void Delete(int id, int siteid) { ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid); if (moduledefinition != null && moduledefinition.SiteId == _alias.SiteId && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server") { // execute uninstall logic or scripts if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType)) { Type moduletype = Type.GetType(moduledefinition.ServerManagerType); if (moduletype != null) { var alias = _tenantManager.GetAlias(); // save current string result = string.Empty; foreach (Tenant tenant in _tenants.GetTenants()) { try { if (moduletype.GetInterface("IInstallable") != null) { _tenantManager.SetTenant(tenant.TenantId); var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype); ((IInstallable)moduleobject).Uninstall(tenant); } else { _sql.ExecuteScript(tenant, moduletype.Assembly, Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + ".Uninstall.sql"); } } catch (Exception ex) { result = "For " + tenant.Name + " " + ex.Message; } } _tenantManager.SetAlias(alias); // restore current if (string.IsNullOrEmpty(result)) { _logger.Log(LogLevel.Information, this, LogFunction.Delete, "{ModuleDefinitionName} Uninstalled For All Tenants", moduledefinition.ModuleDefinitionName); } else { _logger.Log(LogLevel.Error, this, LogFunction.Delete, "Error Uninstalling {ModuleDefinitionName} {Error}", moduledefinition.ModuleDefinitionName, result); } } } // remove module assets if (_installationManager.UninstallPackage(moduledefinition.PackageName)) { _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); } else { // attempt to delete assemblies based on naming convention foreach(string asset in Directory.GetFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), Utilities.GetTypeName(moduledefinition.ModuleDefinitionName) + "*.*")) { System.IO.File.Delete(asset); } _logger.Log(LogLevel.Warning, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}. Please Note That Some Assets May Have Been Missed Due To A Missing Asset Manifest. An Asset Manifest Is Only Created If A Module Is Installed From A Nuget Package.", moduledefinition.Name); } // clean up module static resource folder string assetpath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName)); if (Directory.Exists(assetpath)) { Directory.Delete(assetpath, true); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Static Resources Folder Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName); } // remove PageModule and Module List modulesToRemove = _modules.GetModules(moduledefinition.SiteId).Where(m => m.ModuleDefinitionName == moduledefinition.ModuleDefinitionName).ToList(); foreach (Models.Module moduleToRemove in modulesToRemove) { // Get the PageModule items associated with the Module item to be removed List pageModulesToRemove = _pagemodules.GetPageModules(moduledefinition.SiteId).Where(pm => pm.ModuleId == moduleToRemove.ModuleId).ToList(); foreach(PageModule pageModule in pageModulesToRemove) { // Remove the PageModule item _pagemodules.DeletePageModule(pageModule.PageModuleId); } // Remove Permissions _permissions.DeletePermissions(moduledefinition.SiteId, EntityNames.Module, moduleToRemove.ModuleId); // Remove the Module item _modules.DeleteModule(moduleToRemove.ModuleId); } // remove module definition _moduleDefinitions.DeleteModuleDefinition(id); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.ModuleDefinition, moduledefinition.ModuleDefinitionId, SyncEventActions.Delete); _syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, moduledefinition.SiteId, SyncEventActions.Refresh); _logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Definition {ModuleDefinitionName} Deleted", moduledefinition.Name); } else { _logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Delete Attempt {ModuleDefinitionId}", id); HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden; } } // GET: api//templates [HttpGet("templates")] [Authorize(Roles = RoleNames.Host)] public List