Merge remote-tracking branch 'upstream/dev' into clean-startup

# Conflicts:
#	Oqtane.Client/Program.cs
#	Oqtane.Server/Startup.cs
This commit is contained in:
hishamco
2021-06-11 23:54:38 +03:00
406 changed files with 8385 additions and 4582 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -3,31 +3,28 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using Oqtane.Shared;
using System.Linq;
using System;
using System.Net;
using System.Globalization;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Microsoft.AspNetCore.Http;
using Oqtane.Themes.Controls;
using System.Linq;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class AliasController : Controller
{
private readonly IAliasRepository _aliases;
private readonly IHttpContextAccessor _accessor;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public AliasController(IAliasRepository aliases, IHttpContextAccessor accessor, ISyncManager syncManager, ILogManager logger)
public AliasController(IAliasRepository aliases, ILogManager logger, ITenantManager tenantManager)
{
_aliases = aliases;
_accessor = accessor;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>
@ -35,43 +32,25 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Alias> Get()
{
return _aliases.GetAliases();
var aliases = _aliases.GetAliases();
if (!User.IsInRole(RoleNames.Host))
{
aliases = aliases.Where(item => item.SiteId == _alias.SiteId && item.TenantId == _alias.TenantId);
}
return aliases;
}
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public Alias Get(int id)
{
return _aliases.GetAlias(id);
}
// GET api/<controller>/name/xxx?sync=yyyyMMddHHmmssfff
[HttpGet("name/{**name}")]
public Alias Get(string name, string sync)
{
Alias alias = null;
if (_accessor.HttpContext != null)
{
name = (name == "~") ? "" : name;
name = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(name);
alias = _aliases.GetAlias(name);
}
// get sync events
if (alias != null)
{
alias.SyncDate = DateTime.UtcNow;
alias.SyncEvents = _syncManager.GetSyncEvents(alias.TenantId, DateTime.ParseExact(sync, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture));
}
return alias;
}
// POST api/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public Alias Post([FromBody] Alias alias)
{
if (ModelState.IsValid)
@ -79,12 +58,18 @@ namespace Oqtane.Controllers
alias = _aliases.AddAlias(alias);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Alias Added {Alias}", alias);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Alias Post Attempt {Alias}", alias);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
alias = null;
}
return alias;
}
// PUT api/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public Alias Put(int id, [FromBody] Alias alias)
{
if (ModelState.IsValid)
@ -92,12 +77,18 @@ namespace Oqtane.Controllers
alias = _aliases.UpdateAlias(alias);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Alias Updated {Alias}", alias);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Alias Put Attempt {Alias}", alias);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
alias = null;
}
return alias;
}
// DELETE api/<controller>/5
[HttpDelete("{id}")]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_aliases.DeleteAlias(id);

View File

@ -0,0 +1,35 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Oqtane.Infrastructure;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class DatabaseController : Controller
{
private IOptions<List<Database>> _databaseOptions;
private IConfigManager _config;
public DatabaseController(IOptions<List<Database>> databaseOptions, IConfigManager config)
{
_databaseOptions = databaseOptions;
_config = config;
}
// GET: api/<controller>
[HttpGet]
public IEnumerable<Database> Get()
{
var databases = _databaseOptions.Value;
var master = _config.GetSetting(SettingKeys.DatabaseSection, SettingKeys.DatabaseTypeKey, "");
if (master != "" && databases.Exists(item => item.DBType == master))
{
databases.Find(item => item.DBType == master).IsDefault = true;
}
return databases;
}
}
}

View File

@ -16,31 +16,30 @@ using System.Net;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Microsoft.AspNetCore.Routing.Constraints;
using Oqtane.Extensions;
// ReSharper disable StringIndexOfIsCultureSpecific.1
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class FileController : Controller
{
private readonly IWebHostEnvironment _environment;
private readonly IFileRepository _files;
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ILogManager _logger;
private readonly Alias _alias;
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger)
public FileController(IWebHostEnvironment environment, IFileRepository files, IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
{
_environment = environment;
_files = files;
_folders = folders;
_userPermissions = userPermissions;
_tenants = tenants;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?folder=x
@ -51,11 +50,17 @@ namespace Oqtane.Controllers
int folderid;
if (int.TryParse(folder, out folderid))
{
Folder f = _folders.GetFolder(folderid);
if (f != null && _userPermissions.IsAuthorized(User, PermissionNames.Browse, f.Permissions))
Folder Folder = _folders.GetFolder(folderid);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, Folder.Permissions))
{
files = _files.GetFiles(folderid).ToList();
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FolderId}", folder);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
files = null;
}
}
else
{
@ -70,6 +75,12 @@ namespace Oqtane.Controllers
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {Folder}", folder);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
files = null;
}
}
return files;
@ -79,27 +90,18 @@ namespace Oqtane.Controllers
[HttpGet("{siteId}/{path}")]
public IEnumerable<Models.File> Get(int siteId, string path)
{
var folderPath = WebUtility.UrlDecode(path);
Folder folder = _folders.GetFolder(siteId, folderPath);
List<Models.File> files;
if (folder != null)
Folder folder = _folders.GetFolder(siteId, WebUtility.UrlDecode(path));
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
files = _files.GetFiles(folder.FolderId).ToList();
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {folder}", folder);
HttpContext.Response.StatusCode = 401;
return null;
}
files = _files.GetFiles(folder.FolderId).ToList();
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "Folder Not Found {SiteId} {Path}", siteId, path);
HttpContext.Response.StatusCode = 404;
return null;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {SiteId} {Path}", siteId, path);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
files = null;
}
return files;
@ -110,23 +112,14 @@ namespace Oqtane.Controllers
public Models.File Get(int id)
{
Models.File file = _files.GetFile(id);
if (file != null)
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
{
return file;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {File}", file);
HttpContext.Response.StatusCode = 401;
return null;
}
return file;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id);
HttpContext.Response.StatusCode = 404;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Get Attempt {FileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -136,19 +129,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Models.File Put(int id, [FromBody] Models.File file)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
var File = _files.GetFile(file.FileId, false);
if (ModelState.IsValid && File != null && File.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.FolderId, PermissionNames.Edit))
{
file.Folder = _folders.GetFolder(file.FolderId);
Models.File _file = _files.GetFile(id, false);
if (_file.Name != file.Name || _file.FolderId != file.FolderId)
if (File.Name != file.Name || File.FolderId != file.FolderId)
{
string folderpath = _folders.GetFolderPath(file.Folder);
if (!Directory.Exists(folderpath))
{
Directory.CreateDirectory(folderpath);
}
System.IO.File.Move(_files.GetFilePath(_file), Path.Combine(folderpath, file.Name));
System.IO.File.Move(_files.GetFilePath(File), Path.Combine(folderpath, file.Name));
}
file.Extension = Path.GetExtension(file.Name).ToLower().Replace(".", "");
@ -157,8 +148,8 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update File {File}", file);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Put Attempt {File}", file);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
file = null;
}
@ -171,30 +162,22 @@ namespace Oqtane.Controllers
public void Delete(int id)
{
Models.File file = _files.GetFile(id);
if (file != null)
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
{
if (_userPermissions.IsAuthorized(User, EntityNames.Folder, file.Folder.FolderId, PermissionNames.Edit))
{
_files.DeleteFile(id);
_files.DeleteFile(id);
string filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
System.IO.File.Delete(filepath);
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
}
else
string filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete File {FileId}", id);
HttpContext.Response.StatusCode = 401;
System.IO.File.Delete(filepath);
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "File Deleted {File}", file);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "File Not Found {FileId}", id);
HttpContext.Response.StatusCode = 404;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Delete Attempt {FileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -203,55 +186,57 @@ namespace Oqtane.Controllers
public Models.File UploadFile(string url, string folderid)
{
Models.File file = null;
Folder folder = _folders.GetFolder(int.Parse(folderid));
if (folder == null || !_userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
Folder folder = null;
int FolderId;
if (int.TryParse(folderid, out FolderId))
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Download File {Url} {FolderId}", url, folderid);
HttpContext.Response.StatusCode = 401;
return file;
folder = _folders.GetFolder(FolderId);
}
string folderPath = _folders.GetFolderPath(folder);
CreateDirectory(folderPath);
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
// check for allowable file extensions
if (!Constants.UploadableFiles.Split(',')
.Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, folder.Permissions))
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
HttpContext.Response.StatusCode = (int) HttpStatusCode.Conflict;
return file;
}
string folderPath = _folders.GetFolderPath(folder);
CreateDirectory(folderPath);
if (!filename.IsPathOrFileValid())
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
$"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
HttpContext.Response.StatusCode = (int) HttpStatusCode.Conflict;
return file;
}
try
{
var client = new WebClient();
string targetPath = Path.Combine(folderPath, filename);
// remove file if it already exists
if (System.IO.File.Exists(targetPath))
string filename = url.Substring(url.LastIndexOf("/", StringComparison.Ordinal) + 1);
// check for allowable file extensions
if (!Constants.UploadableFiles.Split(',').Contains(Path.GetExtension(filename).ToLower().Replace(".", "")))
{
System.IO.File.Delete(targetPath);
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url Due To Its File Extension {Url}", url);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
client.DownloadFile(url, targetPath);
file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
if (!filename.IsPathOrFileValid())
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, $"File Could Not Be Downloaded From Url Due To Its File Name Not Allowed {url}");
HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
return file;
}
try
{
var client = new WebClient();
string targetPath = Path.Combine(folderPath, filename);
// remove file if it already exists
if (System.IO.File.Exists(targetPath))
{
System.IO.File.Delete(targetPath);
}
client.DownloadFile(url, targetPath);
file = _files.AddFile(CreateFile(filename, folder.FolderId, targetPath));
}
catch
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "File Could Not Be Downloaded From Url {Url}", url);
}
}
catch
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"File Could Not Be Downloaded From Url {Url}", url);
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {FolderId} {Url}", folderid, url);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return file;
@ -274,24 +259,25 @@ namespace Oqtane.Controllers
string folderPath = "";
if (int.TryParse(folder, out int folderId))
int FolderId;
if (int.TryParse(folder, out FolderId))
{
Folder virtualFolder = _folders.GetFolder(folderId);
if (virtualFolder != null &&
_userPermissions.IsAuthorized(User, PermissionNames.Edit, virtualFolder.Permissions))
Folder Folder = _folders.GetFolder(FolderId);
if (Folder != null && Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Edit, Folder.Permissions))
{
folderPath = _folders.GetFolderPath(virtualFolder);
folderPath = _folders.GetFolderPath(Folder);
}
}
else
{
FolderId = -1;
if (User.IsInRole(RoleNames.Host))
{
folderPath = GetFolderPath(folder);
}
}
if (!String.IsNullOrEmpty(folderPath))
if (!string.IsNullOrEmpty(folderPath))
{
CreateDirectory(folderPath);
using (var stream = new FileStream(Path.Combine(folderPath, file.FileName), FileMode.Create))
@ -300,16 +286,15 @@ namespace Oqtane.Controllers
}
string upload = await MergeFile(folderPath, file.FileName);
if (upload != "" && folderId != -1)
if (upload != "" && FolderId != -1)
{
_files.AddFile(CreateFile(upload, folderId, Path.Combine(folderPath, upload)));
_files.AddFile(CreateFile(upload, FolderId, Path.Combine(folderPath, upload)));
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create,
"User Not Authorized To Upload File {Folder} {File}", folder, file);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Upload Attempt {Folder} {File}", folder, file);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -468,32 +453,26 @@ namespace Oqtane.Controllers
private IActionResult Download(int id, bool asAttachment)
{
var file = _files.GetFile(id);
if (file != null)
if (file != null && file.Folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
{
if (_userPermissions.IsAuthorized(User, PermissionNames.View, file.Folder.Permissions))
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
var filepath = _files.GetFilePath(file);
if (System.IO.File.Exists(filepath))
{
var result = asAttachment
? PhysicalFile(filepath, file.GetMimeType(), file.Name)
: PhysicalFile(filepath, file.GetMimeType());
return result;
}
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
HttpContext.Response.StatusCode = 404;
var result = asAttachment
? PhysicalFile(filepath, file.GetMimeType(), file.Name)
: PhysicalFile(filepath, file.GetMimeType());
return result;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access File {FileId}", id);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FileId} {FilePath}", id, filepath);
HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "File Not Found {FileId}", id);
HttpContext.Response.StatusCode = 404;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized File Access Attempt {FileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
string errorPath = Path.Combine(GetFolderPath("images"), "error.png");
@ -502,7 +481,7 @@ namespace Oqtane.Controllers
private string GetFolderPath(string folder)
{
return Utilities.PathCombine(_environment.WebRootPath, folder);
return Utilities.PathCombine(_environment.ContentRootPath, folder);
}
private void CreateDirectory(string folderpath)

View File

@ -15,22 +15,22 @@ using Microsoft.AspNetCore.Hosting;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class FolderController : Controller
{
private readonly IWebHostEnvironment _environment;
private readonly IFolderRepository _folders;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ILogManager _logger;
private readonly Alias _alias;
public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ITenantResolver tenants, ILogManager logger)
public FolderController(IWebHostEnvironment environment, IFolderRepository folders, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
{
_environment = environment;
_folders = folders;
_userPermissions = userPermissions;
_tenants = tenants;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
@ -38,13 +38,23 @@ namespace Oqtane.Controllers
public IEnumerable<Folder> Get(string siteid)
{
List<Folder> folders = new List<Folder>();
foreach (Folder folder in _folders.GetFolders(int.Parse(siteid)))
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
foreach (Folder folder in _folders.GetFolders(SiteId))
{
folders.Add(folder);
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
folders.Add(folder);
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folders = null;
}
return folders;
}
@ -53,14 +63,14 @@ namespace Oqtane.Controllers
public Folder Get(int id)
{
Folder folder = _folders.GetFolder(id);
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
return folder;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {Folder}", folder);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {FolderId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -74,23 +84,14 @@ namespace Oqtane.Controllers
folderPath = Utilities.PathCombine(folderPath, System.IO.Path.DirectorySeparatorChar.ToString());
}
Folder folder = _folders.GetFolder(siteId, folderPath);
if (folder != null)
if (_userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
return folder;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Folder {Folder}",
folder);
HttpContext.Response.StatusCode = 401;
return null;
}
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.Browse, folder.Permissions))
{
return folder;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "Folder not found {path}",
path);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Get Attempt {Path} For Site {SiteId}", path, siteId);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -100,7 +101,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Folder Post([FromBody] Folder folder)
{
if (ModelState.IsValid)
if (ModelState.IsValid && folder.SiteId == _alias.SiteId)
{
string permissions;
if (folder.ParentId != null)
@ -129,17 +130,23 @@ namespace Oqtane.Controllers
else
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folder = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Folder {Folder}", folder);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Post Attempt {Folder}", folder);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folder = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Post Attempt {Folder}", folder);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folder = null;
}
return folder;
}
@ -148,7 +155,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Folder Put(int id, [FromBody] Folder folder)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
if (ModelState.IsValid && folder.SiteId == _alias.SiteId && _folders.GetFolder(folder.FolderId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Folder, folder.FolderId, PermissionNames.Edit))
{
if (folder.IsPathValid())
{
@ -171,14 +178,14 @@ namespace Oqtane.Controllers
else
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Folder Name Not Valid {Folder}", folder);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folder = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder {Folder}", folder);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Put Attempt {Folder}", folder);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
folder = null;
}
return folder;
@ -189,7 +196,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int folderid, int? parentid)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Folder, folderid, PermissionNames.Edit))
if (siteid == _alias.SiteId && _folders.GetFolder(folderid, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Folder, folderid, PermissionNames.Edit))
{
int order = 1;
List<Folder> folders = _folders.GetFolders(siteid).ToList();
@ -206,8 +213,8 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Folder Order {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Update, "Unauthorized Folder Put Attempt {SiteId} {FolderId} {ParentId}", siteid, folderid, parentid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -216,26 +223,26 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Folder, id, PermissionNames.Edit))
var folder = _folders.GetFolder(id, false);
if (folder != null && folder.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Folder, id, PermissionNames.Edit))
{
Models.Folder _folder = _folders.GetFolder(id, false);
if (Directory.Exists(GetFolderPath(_folder)))
if (Directory.Exists(GetFolderPath(folder)))
{
Directory.Delete(GetFolderPath(_folder));
Directory.Delete(GetFolderPath(folder));
}
_folders.DeleteFolder(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Folder Deleted {FolderId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Folder {FolderId}", id);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Folder Delete Attempt {FolderId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
private string GetFolderPath(Folder folder)
{
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _tenants.GetTenant().TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
return Utilities.PathCombine(_environment.ContentRootPath, "Content", "Tenants", _alias.TenantId.ToString(), "Sites", folder.SiteId.ToString(), folder.Path);
}
}
}

View File

@ -12,11 +12,13 @@ using Oqtane.Modules;
using Oqtane.Shared;
using Oqtane.Themes;
using Microsoft.Extensions.Caching.Memory;
using System.Collections.Generic;
using System.Net;
using Oqtane.Repository;
using Microsoft.AspNetCore.Http;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class InstallationController : Controller
{
private readonly IConfigurationRoot _config;
@ -24,14 +26,18 @@ namespace Oqtane.Controllers
private readonly IDatabaseManager _databaseManager;
private readonly ILocalizationManager _localizationManager;
private readonly IMemoryCache _cache;
private readonly IHttpContextAccessor _accessor;
private readonly IAliasRepository _aliases;
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache)
public InstallationController(IConfigurationRoot config, IInstallationManager installationManager, IDatabaseManager databaseManager, ILocalizationManager localizationManager, IMemoryCache cache, IHttpContextAccessor accessor, IAliasRepository aliases)
{
_config = config;
_installationManager = installationManager;
_databaseManager = databaseManager;
_localizationManager = localizationManager;
_cache = cache;
_accessor = accessor;
_aliases = aliases;
}
// POST api/<controller>
@ -52,11 +58,17 @@ namespace Oqtane.Controllers
return installation;
}
// GET api/<controller>/installed
// GET api/<controller>/installed/?path=xxx
[HttpGet("installed")]
public Installation IsInstalled()
public Installation IsInstalled(string path)
{
return _databaseManager.IsInstalled();
var installation = _databaseManager.IsInstalled();
if (installation.Success)
{
path = _accessor.HttpContext.Request.Host.Value + "/" + WebUtility.UrlDecode(path);
installation.Alias = _aliases.GetAlias(path);
}
return installation;
}
[HttpGet("upgrade")]
@ -82,11 +94,11 @@ namespace Oqtane.Controllers
{
if (_config.GetSection("Runtime").Value == "WebAssembly")
{
return File(GetAssemblies(), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.zip");
return File(GetAssemblies(), System.Net.Mime.MediaTypeNames.Application.Octet, "oqtane.dll");
}
else
{
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -12,7 +12,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class JobController : Controller
{
private readonly IJobRepository _jobs;

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums;
@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class JobLogController : Controller
{
private readonly IJobLogRepository _jobLogs;

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Enums;
@ -9,33 +10,67 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class LanguageController : Controller
{
private readonly ILanguageRepository _languages;
private readonly ILogManager _logger;
private readonly Alias _alias;
public LanguageController(ILanguageRepository language, ILogManager logger)
public LanguageController(ILanguageRepository language, ILogManager logger, ITenantManager tenantManager)
{
_languages = language;
_logger = logger;
_alias = tenantManager.GetAlias();
}
[HttpGet]
public IEnumerable<Language> Get(string siteid) => _languages.GetLanguages(int.Parse(siteid));
public IEnumerable<Language> Get(string siteid)
{
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
return _languages.GetLanguages(SiteId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
[HttpGet("{id}")]
public Language Get(int id) => _languages.GetLanguage(id);
public Language Get(int id)
{
var language = _languages.GetLanguage(id);
if (language != null && language.SiteId == _alias.SiteId)
{
return language;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Get Attempt {LanguageId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
[HttpPost]
[Authorize(Roles = RoleNames.Admin)]
public Language Post([FromBody] Language language)
{
if (ModelState.IsValid)
if (ModelState.IsValid && language.SiteId == _alias.SiteId)
{
language = _languages.AddLanguage(language);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Language Added {Language}", language);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Post Attempt {Language}", language);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
language = null;
}
return language;
}
@ -43,8 +78,18 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_languages.DeleteLanguage(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
var language = _languages.GetLanguage(id);
if (language != null && language.SiteId == _alias.SiteId)
{
_languages.DeleteLanguage(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Language Deleted {LanguageId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Language Delete Attempt {LanguageId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -9,7 +9,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class LocalizationController : Controller
{
private readonly ILocalizationManager _localizationManager;

View File

@ -1,24 +1,28 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Shared;
using Oqtane.Enums;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class LogController : Controller
{
private readonly ILogManager _logger;
private readonly ILogRepository _logs;
private readonly Alias _alias;
public LogController(ILogManager logger, ILogRepository logs)
public LogController(ILogManager logger, ILogRepository logs, ITenantManager tenantManager)
{
_logger = logger;
_logs = logs;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x&level=y&function=z&rows=50
@ -26,7 +30,18 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Log> Get(string siteid, string level, string function, string rows)
{
return _logs.GetLogs(int.Parse(siteid), level, function, int.Parse(rows));
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
return _logs.GetLogs(SiteId, level, function, int.Parse(rows));
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Log Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
@ -34,17 +49,32 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Log Get(int id)
{
return _logs.GetLog(id);
var log = _logs.GetLog(id);
if (log != null && log.SiteId == _alias.SiteId)
{
return log;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Log Get Attempt {LogId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
[HttpPost]
public void Post([FromBody] Log log)
{
if (ModelState.IsValid)
if (ModelState.IsValid && log.SiteId == _alias.SiteId)
{
_logger.Log(log);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Log Post Attempt {Log}", log);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -8,10 +8,11 @@ using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class ModuleController : Controller
{
private readonly IModuleRepository _modules;
@ -20,11 +21,11 @@ namespace Oqtane.Controllers
private readonly IModuleDefinitionRepository _moduleDefinitions;
private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public ModuleController(IModuleRepository modules, IPageModuleRepository pageModules, IPageRepository pages, IModuleDefinitionRepository moduleDefinitions, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_modules = modules;
_pageModules = pageModules;
@ -32,49 +33,61 @@ namespace Oqtane.Controllers
_moduleDefinitions = moduleDefinitions;
_settings = settings;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
[HttpGet]
public IEnumerable<Module> Get(string siteid)
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(int.Parse(siteid)).ToList();
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
List<Module> modules = new List<Module>();
foreach (PageModule pagemodule in _pageModules.GetPageModules(int.Parse(siteid)))
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User,PermissionNames.View, pagemodule.Module.Permissions))
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(SiteId).ToList();
List<Setting> settings = _settings.GetSettings(EntityNames.Module).ToList();
foreach (PageModule pagemodule in _pageModules.GetPageModules(SiteId))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions;
module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy;
module.ModifiedOn = pagemodule.Module.ModifiedOn;
module.IsDeleted = pagemodule.IsDeleted;
if (_userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
{
Module module = new Module();
module.SiteId = pagemodule.Module.SiteId;
module.ModuleDefinitionName = pagemodule.Module.ModuleDefinitionName;
module.AllPages = pagemodule.Module.AllPages;
module.Permissions = pagemodule.Module.Permissions;
module.CreatedBy = pagemodule.Module.CreatedBy;
module.CreatedOn = pagemodule.Module.CreatedOn;
module.ModifiedBy = pagemodule.Module.ModifiedBy;
module.ModifiedOn = pagemodule.Module.ModifiedOn;
module.IsDeleted = pagemodule.IsDeleted;
module.PageModuleId = pagemodule.PageModuleId;
module.ModuleId = pagemodule.ModuleId;
module.PageId = pagemodule.PageId;
module.Title = pagemodule.Title;
module.Pane = pagemodule.Pane;
module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType;
module.PageModuleId = pagemodule.PageModuleId;
module.ModuleId = pagemodule.ModuleId;
module.PageId = pagemodule.PageId;
module.Title = pagemodule.Title;
module.Pane = pagemodule.Pane;
module.Order = pagemodule.Order;
module.ContainerType = pagemodule.ContainerType;
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = settings.Where(item => item.EntityId == pagemodule.ModuleId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
modules.Add(module);
modules.Add(module);
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
modules = null;
}
return modules;
}
@ -83,19 +96,18 @@ namespace Oqtane.Controllers
public Module Get(int id)
{
Module module = _modules.GetModule(id);
if (_userPermissions.IsAuthorized(User,PermissionNames.View, module.Permissions))
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, module.Permissions))
{
List<ModuleDefinition> moduledefinitions = _moduleDefinitions.GetModuleDefinitions(module.SiteId).ToList();
module.ModuleDefinition = moduledefinitions.Find(item => item.ModuleDefinitionName == module.ModuleDefinitionName);
module.Settings = _settings.GetSettings(EntityNames.Module, id)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
return module;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Module {Module}", module);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Get Attempt {ModuleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -105,16 +117,16 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Module Post([FromBody] Module module)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, module.PageId, PermissionNames.Edit))
{
module = _modules.AddModule(module);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Added {Module}", module);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Module {Module}", module);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Post Attempt {Module}", module);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
module = null;
}
return module;
@ -125,7 +137,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Module Put(int id, [FromBody] Module module)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
if (ModelState.IsValid && module.SiteId == _alias.SiteId && _modules.GetModule(module.ModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
{
module = _modules.UpdateModule(module);
if (module.AllPages)
@ -142,12 +154,12 @@ namespace Oqtane.Controllers
}
}
}
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Module {Module}", module);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Put Attempt {Module}", module);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
module = null;
}
return module;
@ -158,16 +170,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public void Delete(int id)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Module, id, PermissionNames.Edit))
var module = _modules.GetModule(id);
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, module.ModuleId, PermissionNames.Edit))
{
_modules.DeleteModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Deleted {ModuleId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Module {ModuleId}", id);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Delete Attempt {ModuleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -177,14 +190,15 @@ namespace Oqtane.Controllers
public string Export(int moduleid)
{
string content = "";
if (_userPermissions.IsAuthorized(User, EntityNames.Module, moduleid, PermissionNames.Edit))
var module = _modules.GetModule(moduleid);
if (module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
{
content = _modules.ExportModule(moduleid);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Other, "User Not Authorized To Export Module {ModuleId}", moduleid);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Export Attempt {ModuleId}", moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return content;
}
@ -195,14 +209,15 @@ namespace Oqtane.Controllers
public bool Import(int moduleid, [FromBody] string content)
{
bool success = false;
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, moduleid, PermissionNames.Edit))
var module = _modules.GetModule(moduleid);
if (ModelState.IsValid && module != null && module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Module, module.ModuleId, PermissionNames.Edit))
{
success = _modules.ImportModule(moduleid, content);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Other, "User Not Authorized To Import Module {ModuleId}", moduleid);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Module Import Attempt {ModuleId}", moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return success;
}

View File

@ -1,21 +1,52 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;
using Oqtane.Infrastructure;
using System.Collections.Generic;
using System;
namespace Oqtane.Controllers
{
public class ModuleControllerBase : Controller
{
protected readonly ILogManager _logger;
protected int _entityId = -1; // passed as a querystring parameter for policy authorization and used for validation
// parameters for policy authorization and validation
protected Dictionary<string, int> _authEntityId = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
protected int _entityId = -1; // legacy support
public ModuleControllerBase(ILogManager logger, IHttpContextAccessor accessor)
{
_logger = logger;
if (accessor.HttpContext.Request.Query.ContainsKey("entityid"))
// populate policy authorization dictionary from querystring
int value;
foreach (var param in accessor.HttpContext.Request.Query)
{
if (param.Key.StartsWith("auth") && param.Key.EndsWith("id") && int.TryParse(param.Value, out value))
{
_authEntityId.Add(param.Key.Substring(4, param.Key.Length - 6), value);
}
}
// legacy support
if (_authEntityId.Count == 0 && accessor.HttpContext.Request.Query.ContainsKey("entityid"))
{
_entityId = int.Parse(accessor.HttpContext.Request.Query["entityid"]);
}
}
protected int AuthEntityId(string entityname)
{
if (_authEntityId.ContainsKey(entityname))
{
return _authEntityId[entityname];
}
else
{
return -1;
}
}
}
}

View File

@ -14,10 +14,11 @@ using Oqtane.Security;
using System;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Json;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class ModuleDefinitionController : Controller
{
private readonly IModuleDefinitionRepository _moduleDefinitions;
@ -27,9 +28,11 @@ namespace Oqtane.Controllers
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment;
private readonly IServiceProvider _serviceProvider;
private readonly ITenantManager _tenantManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ILogManager logger)
public ModuleDefinitionController(IModuleDefinitionRepository moduleDefinitions, ITenantRepository tenants, ISqlRepository sql, IUserPermissions userPermissions, IInstallationManager installationManager, IWebHostEnvironment environment, IServiceProvider serviceProvider, ITenantManager tenantManager, ILogManager logger)
{
_moduleDefinitions = moduleDefinitions;
_tenants = tenants;
@ -38,59 +41,115 @@ namespace Oqtane.Controllers
_installationManager = installationManager;
_environment = environment;
_serviceProvider = serviceProvider;
_tenantManager = tenantManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
[HttpGet]
public IEnumerable<ModuleDefinition> Get(string siteid)
{
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
foreach(ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(int.Parse(siteid)))
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User,PermissionNames.Utilize, moduledefinition.Permissions))
List<ModuleDefinition> moduledefinitions = new List<ModuleDefinition>();
foreach (ModuleDefinition moduledefinition in _moduleDefinitions.GetModuleDefinitions(SiteId))
{
moduledefinitions.Add(moduledefinition);
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions))
{
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;
}
return moduledefinitions;
}
// GET api/<controller>/5?siteid=x
[HttpGet("{id}")]
public ModuleDefinition Get(int id, string siteid)
{
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, int.Parse(siteid));
if (_userPermissions.IsAuthorized(User,PermissionNames.Utilize, moduledefinition.Permissions))
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
return moduledefinition;
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, SiteId);
if (_userPermissions.IsAuthorized(User, PermissionNames.Utilize, moduledefinition.Permissions))
{
return moduledefinition;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Get Attempt {ModuleDefinitionId} {SiteId}", id, siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access ModuleDefinition {ModuleDefinition}", moduledefinition);
HttpContext.Response.StatusCode = 401;
_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/<controller>
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public ModuleDefinition Post([FromBody] ModuleDefinition moduleDefinition)
{
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId)
{
string rootPath;
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", moduleDefinition.Template, Path.DirectorySeparatorChar.ToString());
if (moduleDefinition.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", Oqtane.Client";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, Oqtane.Server";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, moduleDefinition.Owner + "." + moduleDefinition.Name, Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.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/<controller>/5
[HttpPut("{id}")]
[Authorize(Roles = RoleNames.Admin)]
public void Put(int id, [FromBody] ModuleDefinition moduleDefinition)
{
if (ModelState.IsValid)
if (ModelState.IsValid && moduleDefinition.SiteId == _alias.SiteId && _moduleDefinitions.GetModuleDefinition(moduleDefinition.ModuleDefinitionId, moduleDefinition.SiteId) != null)
{
_moduleDefinitions.UpdateModuleDefinition(moduleDefinition);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Module Definition Updated {ModuleDefinition}", moduleDefinition);
}
}
[HttpGet("install")]
[Authorize(Roles = RoleNames.Host)]
public void InstallModules()
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Modules Installed");
_installationManager.InstallPackages("Modules");
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized ModuleDefinition Put Attempt {ModuleDefinition}", moduleDefinition);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
// DELETE api/<controller>/5?siteid=x
@ -99,7 +158,7 @@ namespace Oqtane.Controllers
public void Delete(int id, int siteid)
{
ModuleDefinition moduledefinition = _moduleDefinitions.GetModuleDefinition(id, siteid);
if (moduledefinition != null && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
if (moduledefinition != null && moduledefinition.SiteId == _alias.SiteId && Utilities.GetAssemblyName(moduledefinition.ServerManagerType) != "Oqtane.Server")
{
// execute uninstall logic or scripts
if (!string.IsNullOrEmpty(moduledefinition.ServerManagerType))
@ -111,6 +170,7 @@ namespace Oqtane.Controllers
{
if (moduletype.GetInterface("IInstallable") != null)
{
_tenantManager.SetTenant(tenant.TenantId);
var moduleobject = ActivatorUtilities.CreateInstance(_serviceProvider, moduletype);
((IInstallable)moduleobject).Uninstall(tenant);
}
@ -128,25 +188,8 @@ namespace Oqtane.Controllers
}
// remove module assets
string assetpath = Path.Combine(_environment.WebRootPath, "Modules", Utilities.GetTypeName(moduledefinition.ModuleDefinitionName));
if (System.IO.File.Exists(Path.Combine(assetpath, "assets.json")))
if (_installationManager.UninstallPackage(moduledefinition.PackageName))
{
// use assets.json to clean up file resources
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
assets.Reverse();
foreach(string asset in assets)
{
// legacy support for assets that were stored as absolute paths
string filepath = asset.StartsWith("\\") ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
if (System.IO.File.Exists(filepath))
{
System.IO.File.Delete(filepath);
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
{
Directory.Delete(Path.GetDirectoryName(filepath));
}
}
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Module Assets Removed For {ModuleDefinitionName}", moduledefinition.ModuleDefinitionName);
}
else
@ -160,6 +203,7 @@ namespace Oqtane.Controllers
}
// 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);
@ -170,44 +214,51 @@ namespace Oqtane.Controllers
_moduleDefinitions.DeleteModuleDefinition(id);
_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;
}
}
[HttpGet("install")]
[Authorize(Roles = RoleNames.Host)]
public void InstallModules()
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Modules Installed");
_installationManager.InstallPackages();
}
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> GetTemplates()
public List<Template> GetTemplates()
{
var templates = new List<string>();
var templates = new List<Template>();
var root = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Modules", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
templates.Add(directory.Replace(templatePath, ""));
string name = directory.Replace(templatePath, "");
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
{
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
template.Name = name;
template.Location = "";
if (template.Type.ToLower() != "internal")
{
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
}
templates.Add(template);
}
else
{
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
}
return templates;
}
// POST api/<controller>
[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());
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName , moduleDefinition.Owner + "." + moduleDefinition.Name,Path.DirectorySeparatorChar.ToString());
moduleDefinition.ModuleDefinitionName = moduleDefinition.Owner + "." + moduleDefinition.Name + ", " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Client.Oqtane";
moduleDefinition.ServerManagerType = moduleDefinition.Owner + "." + moduleDefinition.Name + ".Manager." + moduleDefinition.Name + "Manager, " + moduleDefinition.Owner + "." + moduleDefinition.Name + ".Server.Oqtane";
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, moduleDefinition);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Module Definition Created {ModuleDefinition}", moduleDefinition);
}
return moduleDefinition;
}
private void ProcessTemplatesRecursively(DirectoryInfo current, string rootPath, string rootFolder, string templatePath, ModuleDefinition moduleDefinition)
{
// process folder

View File

@ -7,21 +7,24 @@ using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class NotificationController : Controller
{
private readonly INotificationRepository _notifications;
private readonly IUserPermissions _userPermissions;
private readonly ILogManager _logger;
private readonly Alias _alias;
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ILogManager logger)
public NotificationController(INotificationRepository notifications, IUserPermissions userPermissions, ILogManager logger, ITenantManager tenantManager)
{
_notifications = notifications;
_userPermissions = userPermissions;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x&type=y&userid=z
@ -30,17 +33,27 @@ namespace Oqtane.Controllers
public IEnumerable<Notification> Get(string siteid, string direction, string userid)
{
IEnumerable<Notification> notifications = null;
if (IsAuthorized(int.Parse(userid)))
int SiteId;
int UserId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId && int.TryParse(userid, out UserId) && IsAuthorized(UserId))
{
if (direction == "to")
{
notifications = _notifications.GetNotifications(int.Parse(siteid), -1, int.Parse(userid));
notifications = _notifications.GetNotifications(SiteId, -1, UserId);
}
else
{
notifications = _notifications.GetNotifications(int.Parse(siteid), int.Parse(userid), -1);
notifications = _notifications.GetNotifications(SiteId, UserId, -1);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {SiteId} {Direction} {UserId}", siteid, direction, userid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
notifications = null;
}
return notifications;
}
@ -50,11 +63,16 @@ namespace Oqtane.Controllers
public Notification Get(int id)
{
Notification notification = _notifications.GetNotification(id);
if (!(IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
if (notification != null && notification.SiteId == _alias.SiteId && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
{
notification = null;
return notification;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Get Attempt {NotificationId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
return notification;
}
// POST api/<controller>
@ -62,11 +80,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Notification Post([FromBody] Notification notification)
{
if (IsAuthorized(notification.FromUserId))
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && IsAuthorized(notification.FromUserId))
{
notification = _notifications.AddNotification(notification);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Notification Added {NotificationId}", notification.NotificationId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Post Attempt {Notification}", notification);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
notification = null;
}
return notification;
}
@ -75,11 +99,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Notification Put(int id, [FromBody] Notification notification)
{
if (IsAuthorized(notification.FromUserId))
if (ModelState.IsValid && notification.SiteId == _alias.SiteId && _notifications.GetNotification(notification.NotificationId, false) != null && IsAuthorized(notification.FromUserId))
{
notification = _notifications.UpdateNotification(notification);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Notification Updated {NotificationId}", notification.NotificationId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Put Attempt {Notification}", notification);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
notification = null;
}
return notification;
}
@ -89,11 +119,16 @@ namespace Oqtane.Controllers
public void Delete(int id)
{
Notification notification = _notifications.GetNotification(id);
if (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId))
if (notification != null && notification.SiteId == _alias.SiteId && (IsAuthorized(notification.FromUserId) || IsAuthorized(notification.ToUserId)))
{
_notifications.DeleteNotification(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Notification Deleted {NotificationId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Notification Delete Attempt {NotificationId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
private bool IsAuthorized(int? userid)

View File

@ -1,28 +1,33 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Enums;
// ReSharper disable PartialTypeWithSinglePart
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class PackageController : Controller
{
private readonly IInstallationManager _installationManager;
private readonly IWebHostEnvironment _environment;
private readonly ILogManager _logger;
public PackageController(IWebHostEnvironment environment)
public PackageController(IInstallationManager installationManager, IWebHostEnvironment environment, ILogManager logger)
{
_installationManager = installationManager;
_environment = environment;
_logger = logger;
}
// GET: api/<controller>?tag=x
@ -60,7 +65,7 @@ namespace Oqtane.Controllers
{
using (var httpClient = new HttpClient())
{
folder = Path.Combine(_environment.WebRootPath, folder);
folder = Path.Combine(_environment.ContentRootPath, folder);
var response = await httpClient.GetAsync("https://www.nuget.org/api/v2/package/" + packageid.ToLower() + "/" + version).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
string filename = packageid + "." + version + ".nupkg";
@ -86,6 +91,14 @@ namespace Oqtane.Controllers
}
}
}
[HttpGet("install")]
[Authorize(Roles = RoleNames.Host)]
public void InstallPackages()
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Packages Installed");
_installationManager.InstallPackages();
}
}
public partial class SearchResult

View File

@ -13,46 +13,60 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class PageController : Controller
{
private readonly IPageRepository _pages;
private readonly IModuleRepository _modules;
private readonly IPageModuleRepository _pageModules;
private readonly IPermissionRepository _permissionRepository;
private readonly ISettingRepository _settings;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, ISettingRepository settings, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public PageController(IPageRepository pages, IModuleRepository modules, IPageModuleRepository pageModules, IPermissionRepository permissionRepository, ISettingRepository settings, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_pages = pages;
_modules = modules;
_pageModules = pageModules;
_permissionRepository = permissionRepository;
_settings = settings;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
[HttpGet]
public IEnumerable<Page> Get(string siteid)
{
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
List<Page> pages = new List<Page>();
foreach (Page page in _pages.GetPages(int.Parse(siteid)))
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
List<Setting> settings = _settings.GetSettings(EntityNames.Page).ToList();
foreach (Page page in _pages.GetPages(SiteId))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page);
if (_userPermissions.IsAuthorized(User, PermissionNames.View, page.Permissions))
{
page.Settings = settings.Where(item => item.EntityId == page.PageId)
.ToDictionary(setting => setting.SettingName, setting => setting.SettingValue);
pages.Add(page);
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
pages = null;
}
return pages;
}
@ -60,7 +74,7 @@ namespace Oqtane.Controllers
[HttpGet("{id}")]
public Page Get(int id, string userid)
{
Page page;
Page page = null;
if (string.IsNullOrEmpty(userid))
{
page = _pages.GetPage(id);
@ -77,8 +91,8 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Page {Page}", page);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Page}", page);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -88,7 +102,7 @@ namespace Oqtane.Controllers
public Page Get(string path, int siteid)
{
Page page = _pages.GetPage(WebUtility.UrlDecode(path), siteid);
if (page != null)
if (page != null && page.SiteId == _alias.SiteId)
{
if (_userPermissions.IsAuthorized(User,PermissionNames.View, page.Permissions))
{
@ -98,14 +112,15 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Page {Page}", page);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Page}", page);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
else
{
HttpContext.Response.StatusCode = 404;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Get Attempt {Path} for Site {SiteId}", path, siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -115,7 +130,7 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Page Post([FromBody] Page page)
{
if (ModelState.IsValid)
if (ModelState.IsValid && page.SiteId == _alias.SiteId)
{
string permissions;
if (page.ParentId != null)
@ -132,7 +147,7 @@ namespace Oqtane.Controllers
if (_userPermissions.IsAuthorized(User,PermissionNames.Edit, permissions))
{
page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Added {Page}", page);
if (!page.Path.StartsWith("admin/"))
@ -147,11 +162,17 @@ namespace Oqtane.Controllers
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Page {Page}", page);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Warning, this, LogFunction.Create, "User Not Authorized To Add Page {Page}", page);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
page = null;
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Post Attempt {Page}", page);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
page = null;
}
return page;
}
@ -162,7 +183,7 @@ namespace Oqtane.Controllers
{
Page page = null;
Page parent = _pages.GetPage(id);
if (parent != null && parent.IsPersonalizable && _userPermissions.GetUser(User).UserId == int.Parse(userid))
if (parent != null && parent.SiteId == _alias.SiteId && parent.IsPersonalizable && _userPermissions.GetUser(User).UserId == int.Parse(userid))
{
page = new Page();
page.SiteId = parent.SiteId;
@ -183,7 +204,7 @@ namespace Oqtane.Controllers
page.IsPersonalizable = false;
page.UserId = int.Parse(userid);
page = _pages.AddPage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
// copy modules
List<PageModule> pagemodules = _pageModules.GetPageModules(page.SiteId).ToList();
@ -217,6 +238,12 @@ namespace Oqtane.Controllers
_pageModules.AddPageModule(pagemodule);
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Post Attempt {PageId} By User {UserId}", id, userid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
page = null;
}
return page;
}
@ -225,27 +252,87 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public Page Put(int id, [FromBody] Page page)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
if (ModelState.IsValid && page.SiteId == _alias.SiteId && _pages.GetPage(page.PageId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{
// preserve page permissions
var oldPermissions = _permissionRepository.GetPermissions(EntityNames.Page, page.PageId).ToList();
page = _pages.UpdatePage(page);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
// get differences between old and new page permissions
var newPermissions = _permissionRepository.DecodePermissions(page.Permissions, page.SiteId, EntityNames.Page, page.PageId).ToList();
var added = GetPermissionsDifferences(newPermissions, oldPermissions);
var removed = GetPermissionsDifferences(oldPermissions, newPermissions);
// synchronize module permissions
if (added.Count > 0 || removed.Count > 0)
{
foreach (PageModule pageModule in _pageModules.GetPageModules(page.PageId, "").ToList())
{
var modulePermissions = _permissionRepository.GetPermissions(EntityNames.Module, pageModule.Module.ModuleId).ToList();
//var modulePermissions = _permissionRepository.DecodePermissions(pageModule.Module.Permissions, page.SiteId, EntityNames.Module, pageModule.ModuleId).ToList();
// permissions added
foreach(Permission permission in added)
{
if (!modulePermissions.Any(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized))
{
_permissionRepository.AddPermission(new Permission
{
SiteId = page.SiteId,
EntityName = EntityNames.Module,
EntityId = pageModule.ModuleId,
PermissionName = permission.PermissionName,
RoleId = permission.RoleId,
UserId = permission.UserId,
IsAuthorized = permission.IsAuthorized
});
}
}
// permissions removed
foreach (Permission permission in removed)
{
var modulePermission = modulePermissions.FirstOrDefault(item => item.PermissionName == permission.PermissionName
&& item.RoleId == permission.RoleId && item.UserId == permission.UserId && item.IsAuthorized == permission.IsAuthorized);
if (modulePermission != null)
{
_permissionRepository.DeletePermission(modulePermission.PermissionId);
}
}
}
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Updated {Page}", page);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page {Page}", page);
HttpContext.Response.StatusCode = 401;
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Put Attempt {Page}", page);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
page = null;
}
return page;
}
private List<Permission> GetPermissionsDifferences(List<Permission> permissions1, List<Permission> permissions2)
{
var differences = new List<Permission>();
foreach (Permission p in permissions1)
{
if (!permissions2.Any(item => item.PermissionName == p.PermissionName && item.RoleId == p.RoleId && item.UserId == p.UserId && item.IsAuthorized == p.IsAuthorized))
{
differences.Add(p);
}
}
return differences;
}
// PUT api/<controller>/?siteid=x&pageid=y&parentid=z
[HttpPut]
[Authorize(Roles = RoleNames.Registered)]
public void Put(int siteid, int pageid, int? parentid)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
if (siteid == _alias.SiteId && siteid == _alias.SiteId && _pages.GetPage(pageid, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
{
int order = 1;
List<Page> pages = _pages.GetPages(siteid).ToList();
@ -258,13 +345,13 @@ namespace Oqtane.Controllers
}
order += 2;
}
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, siteid);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, siteid);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Order Updated {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page Order {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Put Attempt {SiteId} {PageId} {ParentId}", siteid, pageid, parentid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
@ -274,17 +361,18 @@ namespace Oqtane.Controllers
public void Delete(int id)
{
Page page = _pages.GetPage(id);
if (_userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, page.PageId, PermissionNames.Edit))
{
_pages.DeletePage(page.PageId);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, page.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, page.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Deleted {PageId}", page.PageId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Page {PageId}", page.PageId);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Page Delete Attempt {PageId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -8,25 +8,28 @@ using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Security;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class PageModuleController : Controller
{
private readonly IPageModuleRepository _pageModules;
private readonly IPageRepository _pages;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public PageModuleController(IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public PageModuleController(IPageModuleRepository pageModules, IPageRepository pages, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_pageModules = pageModules;
_pages = pages;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET api/<controller>/5
@ -34,14 +37,14 @@ namespace Oqtane.Controllers
public PageModule Get(int id)
{
PageModule pagemodule = _pageModules.GetPageModule(id);
if (_userPermissions.IsAuthorized(User,PermissionNames.View, pagemodule.Module.Permissions))
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, PermissionNames.View, pagemodule.Module.Permissions))
{
return pagemodule;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access PageModule {PageModule}", pagemodule);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageModuleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -51,14 +54,14 @@ namespace Oqtane.Controllers
public PageModule Get(int pageid, int moduleid)
{
PageModule pagemodule = _pageModules.GetPageModule(pageid, moduleid);
if (_userPermissions.IsAuthorized(User,PermissionNames.View, pagemodule.Module.Permissions))
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User,PermissionNames.View, pagemodule.Module.Permissions))
{
return pagemodule;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access PageModule {PageModule}", pagemodule);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Get Attempt {PageId} {ModuleId}", pageid, moduleid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -68,16 +71,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public PageModule Post([FromBody] PageModule pageModule)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
var page = _pages.GetPage(pageModule.PageId);
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageModule.PageId, PermissionNames.Edit))
{
pageModule = _pageModules.AddPageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Page Module Added {PageModule}", pageModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add PageModule {PageModule}", pageModule);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Post Attempt {PageModule}", pageModule);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
pageModule = null;
}
return pageModule;
@ -88,16 +92,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public PageModule Put(int id, [FromBody] PageModule pageModule)
{
if (ModelState.IsValid && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit))
var page = _pages.GetPage(pageModule.PageId);
if (ModelState.IsValid && page != null && page.SiteId == _alias.SiteId && _pageModules.GetPageModule(pageModule.PageModuleId, false) != null && _userPermissions.IsAuthorized(User, EntityNames.Module, pageModule.ModuleId, PermissionNames.Edit))
{
pageModule = _pageModules.UpdatePageModule(pageModule);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update PageModule {PageModule}", pageModule);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Put Attempt {PageModule}", pageModule);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
pageModule = null;
}
return pageModule;
@ -108,7 +113,8 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Registered)]
public void Put(int pageid, string pane)
{
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
var page = _pages.GetPage(pageid);
if (page != null && page.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pageid, PermissionNames.Edit))
{
int order = 1;
List<PageModule> pagemodules = _pageModules.GetPageModules(pageid, pane).OrderBy(item => item.Order).ToList();
@ -121,14 +127,14 @@ namespace Oqtane.Controllers
}
order += 2;
}
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Page Module Order Updated {PageId} {Pane}", pageid, pane);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Page Module Order {PageId} {Pane}", pageid, pane);
HttpContext.Response.StatusCode = 401;
}
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Put Attempt {PageId} {Pane}", pageid, pane);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
// DELETE api/<controller>/5
@ -137,16 +143,16 @@ namespace Oqtane.Controllers
public void Delete(int id)
{
PageModule pagemodule = _pageModules.GetPageModule(id);
if (_userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
if (pagemodule != null && pagemodule.Module.SiteId == _alias.SiteId && _userPermissions.IsAuthorized(User, EntityNames.Page, pagemodule.PageId, PermissionNames.Edit))
{
_pageModules.DeletePageModule(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Page Module Deleted {PageModuleId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete PageModule {PageModuleId}", id);
HttpContext.Response.StatusCode = 401;
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized PageModule Delete Attempt {PageModuleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums;
@ -6,33 +6,56 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class ProfileController : Controller
{
private readonly IProfileRepository _profiles;
private readonly ILogManager _logger;
private readonly Alias _alias;
public ProfileController(IProfileRepository profiles, ILogManager logger)
public ProfileController(IProfileRepository profiles, ILogManager logger, ITenantManager tenantManager)
{
_profiles = profiles;
_logger = logger;
}
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
[HttpGet]
// GET: api/<controller>?siteid=x
[HttpGet]
public IEnumerable<Profile> Get(string siteid)
{
return _profiles.GetProfiles(int.Parse(siteid));
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
return _profiles.GetProfiles(SiteId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
[HttpGet("{id}")]
public Profile Get(int id)
{
return _profiles.GetProfile(id);
var profile = _profiles.GetProfile(id);
if (profile != null && profile.SiteId == _alias.SiteId)
{
return profile;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Get Attempt {ProfileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
@ -40,11 +63,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Profile Post([FromBody] Profile profile)
{
if (ModelState.IsValid)
if (ModelState.IsValid && profile.SiteId == _alias.SiteId)
{
profile = _profiles.AddProfile(profile);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Profile Added {Profile}", profile);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Post Attempt {Profile}", profile);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
profile = null;
}
return profile;
}
@ -53,11 +82,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Profile Put(int id, [FromBody] Profile profile)
{
if (ModelState.IsValid)
if (ModelState.IsValid && profile.SiteId == _alias.SiteId && _profiles.GetProfile(profile.ProfileId, false) != null)
{
profile = _profiles.UpdateProfile(profile);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Profile Updated {Profile}", profile);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Put Attempt {Profile}", profile);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
profile = null;
}
return profile;
}
@ -66,8 +101,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_profiles.DeleteProfile(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Profile Deleted {ProfileId}", id);
var profile = _profiles.GetProfile(id);
if (profile != null && profile.SiteId == _alias.SiteId)
{
_profiles.DeleteProfile(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Profile Deleted {ProfileId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Profile Delete Attempt {ProfileId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums;
@ -6,35 +6,62 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class RoleController : Controller
{
private readonly IRoleRepository _roles;
private readonly ILogManager _logger;
private readonly Alias _alias;
public RoleController(IRoleRepository roles, ILogManager logger)
public RoleController(IRoleRepository roles, ILogManager logger, ITenantManager tenantManager)
{
_roles = roles;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
// GET: api/<controller>?siteid=x&global=true/false
[HttpGet]
[Authorize(Roles = RoleNames.Registered)]
public IEnumerable<Role> Get(string siteid)
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<Role> Get(string siteid, string global)
{
return _roles.GetRoles(int.Parse(siteid));
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
if (string.IsNullOrEmpty(global))
{
global = "False";
}
return _roles.GetRoles(SiteId, bool.Parse(global));
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = RoleNames.Registered)]
[Authorize(Roles = RoleNames.Admin)]
public Role Get(int id)
{
return _roles.GetRole(id);
var role = _roles.GetRole(id);
if (role != null && role.SiteId == _alias.SiteId)
{
return role;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Get Attempt {RoleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
@ -42,11 +69,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Role Post([FromBody] Role role)
{
if (ModelState.IsValid)
if (ModelState.IsValid && role.SiteId == _alias.SiteId)
{
role = _roles.AddRole(role);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Role Added {Role}", role);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Post Attempt {Role}", role);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
role = null;
}
return role;
}
@ -55,11 +88,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Role Put(int id, [FromBody] Role role)
{
if (ModelState.IsValid)
if (ModelState.IsValid && role.SiteId == _alias.SiteId && _roles.GetRole(role.RoleId, false) != null)
{
role = _roles.UpdateRole(role);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Role Updated {Role}", role);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Put Attempt {Role}", role);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
role = null;
}
return role;
}
@ -68,8 +107,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public void Delete(int id)
{
_roles.DeleteRole(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Role Deleted {RoleId}", id);
var role = _roles.GetRole(id);
if (role != null && !role.IsSystem && role.SiteId == _alias.SiteId)
{
_roles.DeleteRole(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Role Deleted {RoleId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Role Delete Attempt {RoleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -7,27 +7,28 @@ using System.Linq;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class SettingController : Controller
{
private readonly ISettingRepository _settings;
private readonly IPageModuleRepository _pageModules;
private readonly IUserPermissions _userPermissions;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public SettingController(ISettingRepository settings, IPageModuleRepository pageModules, IUserPermissions userPermissions, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_settings = settings;
_pageModules = pageModules;
_userPermissions = userPermissions;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>
@ -42,7 +43,7 @@ namespace Oqtane.Controllers
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Settings {EntityName} {EntityId}", entityname, entityid);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
return settings;
}
@ -59,7 +60,7 @@ namespace Oqtane.Controllers
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Read, "User Not Authorized To Access Setting {Setting}", setting);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
@ -73,14 +74,14 @@ namespace Oqtane.Controllers
setting = _settings.AddSetting(setting);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Setting Added {Setting}", setting);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Create, "User Not Authorized To Add Setting {Setting}", setting);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
setting = null;
}
return setting;
@ -95,14 +96,14 @@ namespace Oqtane.Controllers
setting = _settings.UpdateSetting(setting);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Update, "Setting Updated {Setting}", setting);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update Setting {Setting}", setting);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
setting = null;
}
return setting;
@ -118,14 +119,14 @@ namespace Oqtane.Controllers
_settings.DeleteSetting(id);
if (setting.EntityName == EntityNames.Module)
{
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, _tenants.GetAlias().SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, _alias.SiteId);
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Setting Deleted {Setting}", setting);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Delete, "User Not Authorized To Delete Setting {Setting}", setting);
HttpContext.Response.StatusCode = 401;
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
@ -7,23 +7,24 @@ using System.Linq;
using Oqtane.Enums;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class SiteController : Controller
{
private readonly ISiteRepository _sites;
private readonly ITenantResolver _tenants;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public SiteController(ISiteRepository sites, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public SiteController(ISiteRepository sites, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_sites = sites;
_tenants = tenants;
_syncManager = syncManager;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>
@ -38,7 +39,17 @@ namespace Oqtane.Controllers
[HttpGet("{id}")]
public Site Get(int id)
{
return _sites.GetSite(id);
var site = _sites.GetSite(id);
if (site.SiteId == _alias.SiteId)
{
return site;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Get Attempt {SiteId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
@ -52,8 +63,7 @@ namespace Oqtane.Controllers
{
// provision initial site during installation
authorized = true;
Tenant tenant = _tenants.GetTenant();
site.TenantId = tenant.TenantId;
site.TenantId = _alias.TenantId;
}
else
{
@ -73,12 +83,18 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public Site Put(int id, [FromBody] Site site)
{
if (ModelState.IsValid)
if (ModelState.IsValid && site.SiteId == _alias.SiteId && site.TenantId == _alias.TenantId && _sites.GetSite(site.SiteId, false) != null)
{
site = _sites.UpdateSite(site);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.Site, site.SiteId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.Site, site.SiteId);
_logger.Log(site.SiteId, LogLevel.Information, this, LogFunction.Update, "Site Updated {Site}", site);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Put Attempt {Site}", site);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
site = null;
}
return site;
}
@ -87,8 +103,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Host)]
public void Delete(int id)
{
_sites.DeleteSite(id);
_logger.Log(id, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id);
var site = _sites.GetSite(id);
if (site != null && site.SiteId == _alias.SiteId)
{
_sites.DeleteSite(id);
_logger.Log(id, LogLevel.Information, this, LogFunction.Delete, "Site Deleted {SiteId}", id);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized Site Delete Attempt {SiteId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
@ -7,7 +7,7 @@ using Oqtane.Shared;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class SiteTemplateController : Controller
{
private readonly ISiteTemplateRepository _siteTemplates;

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using System.Collections.Generic;
@ -7,11 +7,11 @@ using Oqtane.Infrastructure;
using Oqtane.Repository;
using Oqtane.Enums;
using System;
using Microsoft.Data.SqlClient;
using System.Data;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class SqlController : Controller
{
private readonly ITenantRepository _tenants;
@ -37,7 +37,7 @@ namespace Oqtane.Controllers
{
foreach (string query in sqlquery.Query.Split("GO", StringSplitOptions.RemoveEmptyEntries))
{
SqlDataReader dr = _sql.ExecuteReader(tenant, query);
IDataReader dr = _sql.ExecuteReader(tenant, query);
_logger.Log(LogLevel.Information, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId}", query, sqlquery.TenantId);
while (dr.Read())
{
@ -52,7 +52,7 @@ namespace Oqtane.Controllers
}
catch (Exception ex)
{
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Results In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
_logger.Log(LogLevel.Error, this, LogFunction.Other, "Sql Query {Query} Executed on Tenant {TenantId} Resulted In An Error {Error}", sqlquery.Query, sqlquery.TenantId, ex.Message);
}
sqlquery.Results = results;
return sqlquery;

View File

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Mvc;
using Oqtane.Models;
using Oqtane.Shared;
using System;
using System.Globalization;
using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.ApiRoute)]
public class SyncController : Controller
{
private readonly ISyncManager _syncManager;
private readonly Alias _alias;
public SyncController(ISyncManager syncManager, ITenantManager tenantManager)
{
_syncManager = syncManager;
_alias = tenantManager.GetAlias();
}
// GET api/<controller>/yyyyMMddHHmmssfff
[HttpGet("{lastSyncDate}")]
public Sync Get(string lastSyncDate)
{
Sync sync = new Sync
{
SyncDate = DateTime.UtcNow,
SyncEvents = _syncManager.GetSyncEvents(_alias.TenantId, DateTime.ParseExact(lastSyncDate, "yyyyMMddHHmmssfff", CultureInfo.InvariantCulture))
};
return sync;
}
}
}

View File

@ -1,20 +1,23 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using Oqtane.Shared;
using System;
using Microsoft.AspNetCore.Hosting;
using Oqtane.Infrastructure;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class SystemController : Controller
{
private readonly IWebHostEnvironment _environment;
private readonly IConfigManager _configManager;
public SystemController(IWebHostEnvironment environment)
public SystemController(IWebHostEnvironment environment, IConfigManager configManager)
{
_environment = environment;
_configManager = configManager;
}
// GET: api/<controller>
@ -23,13 +26,34 @@ namespace Oqtane.Controllers
public Dictionary<string, string> Get()
{
Dictionary<string, string> systeminfo = new Dictionary<string, string>();
systeminfo.Add("rendermode", _configManager.GetSetting("RenderMode", "Server"));
systeminfo.Add("clrversion", Environment.Version.ToString());
systeminfo.Add("osversion", Environment.OSVersion.ToString());
systeminfo.Add("machinename", Environment.MachineName);
systeminfo.Add("serverpath", _environment.ContentRootPath);
systeminfo.Add("servertime", DateTime.Now.ToString());
return systeminfo;
}
[HttpPost]
[Authorize(Roles = RoleNames.Host)]
public void Post([FromBody] Dictionary<string, string> settings)
{
foreach(KeyValuePair<string, string> kvp in settings)
{
switch (kvp.Key)
{
case "runtime":
_configManager.AddOrUpdateSetting("Runtime", kvp.Value, false);
break;
case "rendermode":
_configManager.AddOrUpdateSetting("RenderMode", kvp.Value, false);
break;
}
}
}
}
}

View File

@ -1,4 +1,4 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Models;
using System.Collections.Generic;
@ -9,7 +9,7 @@ using Oqtane.Repository;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class TenantController : Controller
{
private readonly ITenantRepository _tenants;
@ -23,7 +23,7 @@ namespace Oqtane.Controllers
// GET: api/<controller>
[HttpGet]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public IEnumerable<Tenant> Get()
{
return _tenants.GetTenants();
@ -31,7 +31,7 @@ namespace Oqtane.Controllers
// GET api/<controller>/5
[HttpGet("{id}")]
[Authorize(Roles = RoleNames.Admin)]
[Authorize(Roles = RoleNames.Host)]
public Tenant Get(int id)
{
return _tenants.GetTenant(id);

View File

@ -16,7 +16,7 @@ using System.Text.Json;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class ThemeController : Controller
{
private readonly IThemeRepository _themes;
@ -45,7 +45,7 @@ namespace Oqtane.Controllers
public void InstallThemes()
{
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Themes Installed");
_installationManager.InstallPackages("Themes");
_installationManager.InstallPackages();
}
// DELETE api/<controller>/xxx
@ -58,25 +58,8 @@ namespace Oqtane.Controllers
if (theme != null && Utilities.GetAssemblyName(theme.ThemeName) != "Oqtane.Client")
{
// remove theme assets
string assetpath = Path.Combine(_environment.WebRootPath, "Themes", Utilities.GetTypeName(theme.ThemeName));
if (System.IO.File.Exists(Path.Combine(assetpath, "assets.json")))
if (_installationManager.UninstallPackage(theme.PackageName))
{
// use assets.json to clean up file resources
List<string> assets = JsonSerializer.Deserialize<List<string>>(System.IO.File.ReadAllText(Path.Combine(assetpath, "assets.json")));
assets.Reverse();
foreach (string asset in assets)
{
// legacy support for assets that were stored as absolute paths
string filepath = (asset.StartsWith("\\")) ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
if (System.IO.File.Exists(filepath))
{
System.IO.File.Delete(filepath);
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
{
Directory.Delete(Path.GetDirectoryName(filepath));
}
}
}
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Assets Removed For {ThemeName}", theme.ThemeName);
}
else
@ -90,10 +73,10 @@ namespace Oqtane.Controllers
}
// clean up theme static resource folder
string folder = Path.Combine(_environment.WebRootPath, "Themes" , Utilities.GetTypeName(theme.ThemeName));
if (Directory.Exists(folder))
string assetpath = Path.Combine(_environment.WebRootPath, "Themes", Utilities.GetTypeName(theme.ThemeName));
if (Directory.Exists(assetpath))
{
Directory.Delete(folder, true);
Directory.Delete(assetpath, true);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "Theme Static Resource Folder Removed For {ThemeName}", theme.ThemeName);
}
@ -106,13 +89,29 @@ namespace Oqtane.Controllers
// GET: api/<controller>/templates
[HttpGet("templates")]
[Authorize(Roles = RoleNames.Host)]
public List<string> GetTemplates()
public List<Template> GetTemplates()
{
var templates = new List<string>();
var templates = new List<Template>();
var root = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", Path.DirectorySeparatorChar.ToString());
foreach (string directory in Directory.GetDirectories(templatePath))
{
templates.Add(directory.Replace(templatePath, ""));
string name = directory.Replace(templatePath, "");
if (System.IO.File.Exists(Path.Combine(directory, "template.json")))
{
var template = JsonSerializer.Deserialize<Template>(System.IO.File.ReadAllText(Path.Combine(directory, "template.json")));
template.Name = name;
template.Location = "";
if (template.Type.ToLower() != "internal")
{
template.Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString());
}
templates.Add(template);
}
else
{
templates.Add(new Template { Name = name, Title = name, Type = "External", Version = "", Location = Utilities.PathCombine(root.Parent.ToString(), Path.DirectorySeparatorChar.ToString()) });
}
}
return templates;
}
@ -128,8 +127,16 @@ namespace Oqtane.Controllers
DirectoryInfo rootFolder = Directory.GetParent(_environment.ContentRootPath);
string templatePath = Utilities.PathCombine(_environment.WebRootPath, "Themes", "Templates", theme.Template, Path.DirectorySeparatorChar.ToString());
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + "." + theme.Name, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + "." + theme.Name + ", " + theme.Owner + "." + theme.Name + ".Client.Oqtane";
if (theme.Template.ToLower().Contains("internal"))
{
rootPath = Utilities.PathCombine(rootFolder.FullName, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + "." + theme.Name + ", Oqtane.Client";
}
else
{
rootPath = Utilities.PathCombine(rootFolder.Parent.FullName, theme.Owner + "." + theme.Name, Path.DirectorySeparatorChar.ToString());
theme.ThemeName = theme.Owner + "." + theme.Name + ", " + theme.Owner + "." + theme.Name + ".Client.Oqtane";
}
ProcessTemplatesRecursively(new DirectoryInfo(templatePath), rootPath, rootFolder.Name, templatePath, theme);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "Theme Created {Theme}", theme);

View File

@ -18,7 +18,7 @@ using Oqtane.Extensions;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class UserController : Controller
{
private readonly IUserRepository _users;
@ -26,26 +26,26 @@ namespace Oqtane.Controllers
private readonly IUserRoleRepository _userRoles;
private readonly UserManager<IdentityUser> _identityUserManager;
private readonly SignInManager<IdentityUser> _identitySignInManager;
private readonly ITenantResolver _tenants;
private readonly INotificationRepository _notifications;
private readonly IFolderRepository _folders;
private readonly ISyncManager _syncManager;
private readonly ISiteRepository _sites;
private readonly ILogManager _logger;
private readonly Alias _alias;
public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantResolver tenants, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, ILogManager logger)
public UserController(IUserRepository users, IRoleRepository roles, IUserRoleRepository userRoles, UserManager<IdentityUser> identityUserManager, SignInManager<IdentityUser> identitySignInManager, ITenantManager tenantManager, INotificationRepository notifications, IFolderRepository folders, ISyncManager syncManager, ISiteRepository sites, ILogManager logger)
{
_users = users;
_roles = roles;
_userRoles = userRoles;
_identityUserManager = identityUserManager;
_identitySignInManager = identitySignInManager;
_tenants = tenants;
_folders = folders;
_notifications = notifications;
_syncManager = syncManager;
_sites = sites;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET api/<controller>/5?siteid=x
@ -53,26 +53,46 @@ namespace Oqtane.Controllers
[Authorize]
public User Get(int id, string siteid)
{
User user = _users.GetUser(id);
if (user != null)
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId);
User user = _users.GetUser(id);
if (user != null)
{
user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId);
}
return Filter(user);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Get Attempt {UserId} {SiteId}", id, siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
return Filter(user);
}
// GET api/<controller>/name/x?siteid=x
[HttpGet("name/{name}")]
public User Get(string name, string siteid)
{
User user = _users.GetUser(name);
if (user != null)
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId);
User user = _users.GetUser(name);
if (user != null)
{
user.SiteId = int.Parse(siteid);
user.Roles = GetUserRoles(user.UserId, user.SiteId);
}
return Filter(user);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Get Attempt {Username} {SiteId}", name, siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
return Filter(user);
}
private User Filter(User user)
@ -102,13 +122,18 @@ namespace Oqtane.Controllers
[HttpPost]
public async Task<User> Post([FromBody] User user)
{
if (ModelState.IsValid)
if (ModelState.IsValid && user.SiteId == _alias.SiteId)
{
var newUser = await CreateUser(user);
return newUser;
var User = await CreateUser(user);
return User;
}
else
{
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Post Attempt {User}", user);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
return null;
}
private async Task<User> CreateUser(User user)
@ -146,7 +171,7 @@ namespace Oqtane.Controllers
if (!verified)
{
string token = await _identityUserManager.GenerateEmailConfirmationTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string url = HttpContext.Request.Scheme + "://" + _alias.Name + "/login?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nIn Order To Complete The Registration Of Your User Account Please Click The Link Displayed Below:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, null, newUser, "User Account Verification", body, null);
_notifications.AddNotification(notification);
@ -173,6 +198,7 @@ namespace Oqtane.Controllers
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Type = FolderTypes.Private,
Path = Utilities.PathCombine(folder.Path, newUser.UserId.ToString(),Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
@ -229,30 +255,28 @@ namespace Oqtane.Controllers
[Authorize]
public async Task<User> Put(int id, [FromBody] User user)
{
if (ModelState.IsValid)
if (ModelState.IsValid && user.SiteId == _alias.SiteId && _users.GetUser(user.UserId, false) != null && (User.IsInRole(RoleNames.Admin) || User.Identity.Name == user.Username))
{
if (User.IsInRole(RoleNames.Admin) || User.Identity.Name == user.Username)
if (user.Password != "")
{
if (user.Password != "")
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
if (identityuser != null)
{
IdentityUser identityuser = await _identityUserManager.FindByNameAsync(user.Username);
if (identityuser != null)
{
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
await _identityUserManager.UpdateAsync(identityuser);
}
identityuser.PasswordHash = _identityUserManager.PasswordHasher.HashPassword(identityuser, user.Password);
await _identityUserManager.UpdateAsync(identityuser);
}
user = _users.UpdateUser(user);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, user.UserId);
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Update, "User Not Authorized To Update User {User}", user);
HttpContext.Response.StatusCode = 401;
user = null;
}
user = _users.UpdateUser(user);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, user.UserId);
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Updated {User}", user);
}
else
{
user.Password = ""; // remove sensitive information
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Post Attempt {User}", user);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
user = null;
}
return user;
}
@ -262,18 +286,19 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public async Task Delete(int id, string siteid)
{
int SiteId;
User user = _users.GetUser(id);
if (user != null)
if (user != null && int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
// remove user roles for site
foreach (UserRole userrole in _userRoles.GetUserRoles(user.UserId, Int32.Parse(siteid)).ToList())
foreach (UserRole userrole in _userRoles.GetUserRoles(user.UserId, SiteId).ToList())
{
_userRoles.DeleteUserRole(userrole.UserRoleId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userrole);
}
// remove user folder for site
var folder = _folders.GetFolder(Int32.Parse(siteid), Utilities.PathCombine("Users", user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()));
var folder = _folders.GetFolder(SiteId, Utilities.PathCombine("Users", user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()));
if (folder != null)
{
if (Directory.Exists(_folders.GetFolderPath(folder)))
@ -306,6 +331,11 @@ namespace Oqtane.Controllers
}
}
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Delete Attempt {UserId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
// POST api/<controller>/login
@ -358,7 +388,7 @@ namespace Oqtane.Controllers
[Authorize]
public async Task Logout([FromBody] User user)
{
await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
await HttpContext.SignOutAsync(Constants.AuthenticationScheme);
_logger.Log(LogLevel.Information, this, LogFunction.Security, "User Logout {Username}", (user != null) ? user.Username : "");
}
@ -401,7 +431,7 @@ namespace Oqtane.Controllers
if (identityuser != null)
{
string token = await _identityUserManager.GeneratePasswordResetTokenAsync(identityuser);
string url = HttpContext.Request.Scheme + "://" + _tenants.GetAlias().Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string url = HttpContext.Request.Scheme + "://" + _alias.Name + "/reset?name=" + user.Username + "&token=" + WebUtility.UrlEncode(token);
string body = "Dear " + user.DisplayName + ",\n\nPlease Click The Link Displayed Below To Reset Your Password:\n\n" + url + "\n\nThank You!";
var notification = new Notification(user.SiteId, null, user, "User Password Reset", body, null);
_notifications.AddNotification(notification);
@ -444,7 +474,7 @@ namespace Oqtane.Controllers
return user;
}
// GET api/<controller>/current
// GET api/<controller>/authenticate
[HttpGet("authenticate")]
public User Authenticate()
{

View File

@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Oqtane.Enums;
@ -6,23 +6,27 @@ using Oqtane.Models;
using Oqtane.Shared;
using Oqtane.Infrastructure;
using Oqtane.Repository;
using System.Linq;
using System.Net;
namespace Oqtane.Controllers
{
[Route(ControllerRoutes.Default)]
[Route(ControllerRoutes.ApiRoute)]
public class UserRoleController : Controller
{
private readonly IUserRoleRepository _userRoles;
private readonly ITenantResolver _tenants;
private readonly IRoleRepository _roles;
private readonly ISyncManager _syncManager;
private readonly ILogManager _logger;
private readonly Alias _alias;
public UserRoleController(IUserRoleRepository userRoles, ITenantResolver tenants, ISyncManager syncManager, ILogManager logger)
public UserRoleController(IUserRoleRepository userRoles, IRoleRepository roles, ITenantManager tenantManager, ISyncManager syncManager, ILogManager logger)
{
_userRoles = userRoles;
_roles = roles;
_syncManager = syncManager;
_tenants = tenants;
_logger = logger;
_alias = tenantManager.GetAlias();
}
// GET: api/<controller>?siteid=x
@ -30,7 +34,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public IEnumerable<UserRole> Get(string siteid)
{
return _userRoles.GetUserRoles(int.Parse(siteid));
int SiteId;
if (int.TryParse(siteid, out SiteId) && SiteId == _alias.SiteId)
{
return _userRoles.GetUserRoles(SiteId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Get Attempt {SiteId}", siteid);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// GET api/<controller>/5
@ -38,7 +52,17 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public UserRole Get(int id)
{
return _userRoles.GetUserRole(id);
var userrole = _userRoles.GetUserRole(id);
if (userrole != null && userrole.Role.SiteId == _alias.SiteId)
{
return userrole;
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Get Attempt {UserRoleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
return null;
}
}
// POST api/<controller>
@ -46,11 +70,26 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public UserRole Post([FromBody] UserRole userRole)
{
if (ModelState.IsValid)
var role = _roles.GetRole(userRole.RoleId);
if (ModelState.IsValid && role != null && role.SiteId == _alias.SiteId && (User.IsInRole(RoleNames.Host) || role.Name != RoleNames.Host))
{
if (role.Name == RoleNames.Host)
{
// host roles can only exist at global level - remove all site specific user roles
_userRoles.DeleteUserRoles(userRole.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Roles Deleted For UserId {UserId}", userRole.UserId);
}
userRole = _userRoles.AddUserRole(userRole);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized UserRole Post Attempt {UserRole}", userRole);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
userRole = null;
}
return userRole;
}
@ -60,12 +99,19 @@ namespace Oqtane.Controllers
[Authorize(Roles = RoleNames.Admin)]
public UserRole Put(int id, [FromBody] UserRole userRole)
{
if (ModelState.IsValid)
var role = _roles.GetRole(userRole.RoleId);
if (ModelState.IsValid && role != null && role.SiteId == _alias.SiteId && _userRoles.GetUserRole(userRole.UserRoleId, false) != null && (User.IsInRole(RoleNames.Host) || role.Name != RoleNames.Host))
{
userRole = _userRoles.UpdateUserRole(userRole);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId);
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Update, "User Role Updated {UserRole}", userRole);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Put Attempt {UserRole}", userRole);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
userRole = null;
}
return userRole;
}
@ -75,9 +121,29 @@ namespace Oqtane.Controllers
public void Delete(int id)
{
UserRole userRole = _userRoles.GetUserRole(id);
_userRoles.DeleteUserRole(id);
_syncManager.AddSyncEvent(_tenants.GetTenant().TenantId, EntityNames.User, userRole.UserId);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userRole);
if (userRole != null && userRole.Role.SiteId == _alias.SiteId && (User.IsInRole(RoleNames.Host) || userRole.Role.Name != RoleNames.Host))
{
_userRoles.DeleteUserRole(id);
_logger.Log(LogLevel.Information, this, LogFunction.Delete, "User Role Deleted {UserRole}", userRole);
if (userRole.Role.Name == RoleNames.Host)
{
// add site specific user roles to preserve user access
var role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Registered);
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
role = _roles.GetRoles(_alias.SiteId).FirstOrDefault(item => item.Name == RoleNames.Admin);
userRole = _userRoles.AddUserRole(new UserRole { UserId = userRole.UserId, RoleId = role.RoleId, EffectiveDate = null, ExpiryDate = null });
_logger.Log(LogLevel.Information, this, LogFunction.Create, "User Role Added {UserRole}", userRole);
}
_syncManager.AddSyncEvent(_alias.TenantId, EntityNames.User, userRole.UserId);
}
else
{
_logger.Log(LogLevel.Error, this, LogFunction.Security, "Unauthorized User Role Delete Attempt {UserRoleId}", id);
HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
namespace Oqtane.Databases
{
public abstract class DatabaseBase : IDatabase
{
private static string _assemblyName;
private static string _typeName;
protected DatabaseBase(string name, string friendlyName)
{
Name = name;
FriendlyName = friendlyName;
}
protected static void Initialize(Type type)
{
var typeQualifiedName = type.AssemblyQualifiedName;
var assembly = type.Assembly;
var assemblyName = assembly.FullName;
_typeName = typeQualifiedName.Substring(0, typeQualifiedName.IndexOf(", Version"));
_assemblyName = assemblyName.Substring(0, assemblyName.IndexOf(", Version"));
}
public string AssemblyName => _assemblyName;
public string FriendlyName { get; }
public string Name { get; }
public abstract string Provider { get; }
public string TypeName => _typeName;
public abstract OperationBuilder<AddColumnOperation> AddAutoIncrementColumn(ColumnsBuilder table, string name);
public virtual string ConcatenateSql(params string[] values)
{
var returnValue = String.Empty;
for (var i = 0; i < values.Length; i++)
{
if (i > 0)
{
returnValue += " + ";
}
returnValue += values[i];
}
return returnValue;
}
public abstract int ExecuteNonQuery(string connectionString, string query);
public abstract IDataReader ExecuteReader(string connectionString, string query);
public virtual string RewriteName(string name)
{
return name;
}
public virtual void UpdateIdentityStoreTableNames(ModelBuilder builder)
{
}
public abstract DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
}
}

View File

@ -0,0 +1,34 @@
using System.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
namespace Oqtane.Databases.Interfaces
{
public interface IDatabase
{
public string AssemblyName { get; }
public string FriendlyName { get; }
public string Name { get; }
public string Provider { get; }
public string TypeName { get; }
public OperationBuilder<AddColumnOperation> AddAutoIncrementColumn(ColumnsBuilder table, string name);
public string ConcatenateSql(params string[] values);
public int ExecuteNonQuery(string connectionString, string query);
public IDataReader ExecuteReader(string connectionString, string query);
public string RewriteName(string name);
public void UpdateIdentityStoreTableNames(ModelBuilder builder);
public DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString);
}
}

View File

@ -1,10 +1,11 @@
using System.Collections.Generic;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
namespace Oqtane.Repository.Databases.Interfaces
{
public interface IMultiDatabase
{
public IEnumerable<IOqtaneDatabase> Databases { get; }
public IDatabase ActiveDatabase { get; }
}
}

View File

@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using Oqtane.Models;
using Oqtane.Repository.Databases;
namespace Oqtane.Databases
{
public class LocalDbDatabase : SqlServerDatabaseBase
{
private static string _friendlyName => "Local Database";
private static string _name => "LocalDB";
private static readonly List<ConnectionStringField> _connectionStringFields = new()
{
new() {Name = "Server", FriendlyName = "Server", Value = "(LocalDb)\\MSSQLLocalDB", HelpText="Enter the database server"},
new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"}
};
public LocalDbDatabase() :base(_name, _friendlyName, _connectionStringFields) { }
public override string BuildConnectionString()
{
var connectionString = String.Empty;
var server = ConnectionStringFields[0].Value;
var database = ConnectionStringFields[1].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database))
{
connectionString = $"Data Source={server};AttachDbFilename=|DataDirectory|\\{database}.mdf;Initial Catalog={database};Integrated Security=SSPI;";
}
return connectionString;
}
}
}

View File

@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using Oqtane.Models;
using Oqtane.Repository.Databases;
// ReSharper disable ArrangeObjectCreationWhenTypeNotEvident
namespace Oqtane.Databases
{
public class SqlServerDatabase : SqlServerDatabaseBase
{
private static string _friendlyName => "SQL Server";
private static string _name => "SqlServer";
private static readonly List<ConnectionStringField> _connectionStringFields = new()
{
new() {Name = "Server", FriendlyName = "Server", Value = ".", HelpText="Enter the database server"},
new() {Name = "Database", FriendlyName = "Database", Value = "Oqtane-{{Date}}", HelpText="Enter the name of the database"},
new() {Name = "IntegratedSecurity", FriendlyName = "Integrated Security", Value = "true", HelpText="Select if you want integrated security or not"},
new() {Name = "Uid", FriendlyName = "User Id", Value = "", HelpText="Enter the username to use for the database"},
new() {Name = "Pwd", FriendlyName = "Password", Value = "", HelpText="Enter the password to use for the database"}
};
public SqlServerDatabase() :base(_name, _friendlyName, _connectionStringFields) { }
public override string BuildConnectionString()
{
var connectionString = String.Empty;
var server = ConnectionStringFields[0].Value;
var database = ConnectionStringFields[1].Value;
var integratedSecurity = Boolean.Parse(ConnectionStringFields[2].Value);
var userId = ConnectionStringFields[3].Value;
var password = ConnectionStringFields[4].Value;
if (!String.IsNullOrEmpty(server) && !String.IsNullOrEmpty(database))
{
connectionString = $"Data Source={server};Initial Catalog={database};";
}
if (integratedSecurity)
{
connectionString += "Integrated Security=SSPI;";
}
else
{
if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(password))
{
connectionString += $"User ID={userId};Password={password};";
}
else
{
connectionString = String.Empty;
}
}
return connectionString;
}
}
}

View File

@ -1,30 +0,0 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases;
using Oqtane.Interfaces;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Repository.Databases
{
public abstract class SqlServerDatabaseBase : OqtaneDatabaseBase
{
protected SqlServerDatabaseBase(string name, string friendlyName, List<ConnectionStringField> connectionStringFields) : base(name, friendlyName, connectionStringFields)
{
}
public override string Provider => "Microsoft.EntityFrameworkCore.SqlServer";
public override OperationBuilder<AddColumnOperation> AddAutoIncrementColumn(ColumnsBuilder table, string name)
{
return table.Column<int>(name: name, nullable: false).Annotation("SqlServer:Identity", "1, 1");
}
public override DbContextOptionsBuilder UseDatabase(DbContextOptionsBuilder optionsBuilder, string connectionString)
{
return optionsBuilder.UseSqlServer(connectionString);
}
}
}

View File

@ -38,5 +38,8 @@ namespace Oqtane.Extensions
return app;
}
public static IApplicationBuilder UseTenantResolution(this IApplicationBuilder builder)
=> builder.UseMiddleware<TenantMiddleware>();
}
}

View File

@ -1,29 +1,17 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
// ReSharper disable ConvertToUsingDeclaration
namespace Oqtane.Extensions
{
public static class DbContextOptionsBuilderExtensions
{
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, IOqtaneDatabase database, string connectionString)
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, IDatabase database, string connectionString)
{
database.UseDatabase(optionsBuilder, connectionString);
return optionsBuilder;
}
public static DbContextOptionsBuilder UseOqtaneDatabase([NotNull] this DbContextOptionsBuilder optionsBuilder, string databaseType, string connectionString)
{
var type = Type.GetType(databaseType);
var database = Activator.CreateInstance(type) as IOqtaneDatabase;
database.UseDatabase(optionsBuilder, connectionString);
return optionsBuilder;
}
}
}

View File

@ -200,17 +200,6 @@ namespace Microsoft.Extensions.DependencyInjection
}
}
// dynamically register database providers
var databaseTypes = assembly.GetInterfaces<IOqtaneDatabase>();
foreach (var databaseType in databaseTypes)
{
if (databaseType.AssemblyQualifiedName != null)
{
var serviceType = Type.GetType("Oqtane.Interfaces.IOqtaneDatabase, Oqtane.Shared");
services.AddScoped(serviceType ?? databaseType, databaseType);
}
}
// dynamically register hosted services
var serviceTypes = assembly.GetTypes(hostedServiceType);
foreach (var serviceType in serviceTypes)
@ -265,26 +254,7 @@ namespace Microsoft.Extensions.DependencyInjection
if (!assemblies.Any(a => AssemblyName.ReferenceMatchesDefinition(assemblyName, a.GetName())))
{
try
{
var pdb = Path.ChangeExtension(dll.FullName, ".pdb");
Assembly assembly = null;
// load assembly ( and symbols ) from stream to prevent locking files ( as long as dependencies are in /bin they will load as well )
if (File.Exists(pdb))
{
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)), new MemoryStream(File.ReadAllBytes(pdb)));
}
else
{
assembly = AssemblyLoadContext.Default.LoadFromStream(new MemoryStream(File.ReadAllBytes(dll.FullName)));
}
Console.WriteLine($"Loaded : {assemblyName}");
}
catch (Exception e)
{
Console.WriteLine($"Failed : {assemblyName}\n{e}");
}
AssemblyLoadContext.Default.LoadOqtaneAssembly(dll, assemblyName);
}
}
}

View File

@ -0,0 +1,115 @@
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace Oqtane.Infrastructure
{
public class ConfigManager : IConfigManager
{
private readonly IConfigurationRoot _config;
public ConfigManager(IConfigurationRoot config)
{
_config = config;
}
public IConfigurationSection GetSection(string key)
{
return _config.GetSection(key);
}
public T GetSetting<T>(string sectionKey, T defaultValue)
{
return GetSetting(sectionKey, "", defaultValue);
}
public T GetSetting<T>(string sectionKey, string settingKey, T defaultValue)
{
T value;
if (!string.IsNullOrEmpty(settingKey))
{
value = _config.GetSection(sectionKey).GetValue(settingKey, defaultValue);
}
else
{
value = _config.GetValue(sectionKey, defaultValue);
}
if (value == null) value = defaultValue;
return value;
}
public void AddOrUpdateSetting<T>(string key, T value, bool reload)
{
AddOrUpdateSetting("appsettings.json", key, value, reload);
}
public void AddOrUpdateSetting<T>(string file, string key, T value, bool reload)
{
try
{
var path = Path.Combine(Directory.GetCurrentDirectory(), file);
dynamic jsonObj = JsonConvert.DeserializeObject(File.ReadAllText(path));
SetValueRecursively(key, jsonObj, value, "set");
File.WriteAllText(path, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
if (reload) Reload();
}
catch (Exception ex)
{
Console.WriteLine("Error modifying app settings {0}", ex);
}
}
public void RemoveSetting(string key, bool reload)
{
RemoveSetting("appsettings.json", key, reload);
}
public void RemoveSetting(string file, string key, bool reload)
{
try
{
var path = Path.Combine(Directory.GetCurrentDirectory(), file);
dynamic jsonObj = JsonConvert.DeserializeObject(File.ReadAllText(path));
SetValueRecursively(key, jsonObj, "", "remove");
File.WriteAllText(path, JsonConvert.SerializeObject(jsonObj, Formatting.Indented));
if (reload) Reload();
}
catch (Exception ex)
{
Console.WriteLine("Error modifying app settings {0}", ex);
}
}
private void SetValueRecursively<T>(string key, dynamic jsonObj, T value, string action)
{
var remainingSections = key.Split(":", 2);
var currentSection = remainingSections[0];
if (remainingSections.Length > 1)
{
var nextSection = remainingSections[1];
jsonObj[currentSection] ??= new JObject();
SetValueRecursively(nextSection, jsonObj[currentSection], value, action);
}
else
{
switch (action)
{
case "set":
jsonObj[currentSection] = value;
break;
case "remove":
jsonObj.Property(currentSection).Remove();
break;
}
}
}
public void Reload()
{
_config.Reload();
}
}
}

View File

@ -3,19 +3,20 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Oqtane.Databases.Interfaces;
using Oqtane.Extensions;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
using Oqtane.Enums;
using Oqtane.Interfaces;
using File = System.IO.File;
using Newtonsoft.Json;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable ConvertToUsingDeclaration
@ -28,24 +29,28 @@ namespace Oqtane.Infrastructure
{
private readonly IConfigurationRoot _config;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IWebHostEnvironment _environment;
private readonly IMemoryCache _cache;
private readonly IConfigManager _configManager;
public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IMemoryCache cache)
public DatabaseManager(IConfigurationRoot config, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IMemoryCache cache, IConfigManager configManager)
{
_config = config;
_serviceScopeFactory = serviceScopeFactory;
_environment = environment;
_cache = cache;
_configManager = configManager;
}
public Installation IsInstalled()
{
var result = new Installation { Success = false, Message = string.Empty };
if (!string.IsNullOrEmpty(_config.GetConnectionString(SettingKeys.ConnectionStringKey)))
{
result.Success = true;
using (var scope = _serviceScopeFactory.CreateScope())
using (var db = GetInstallationContext())
{
var db = scope.ServiceProvider.GetRequiredService<MasterDBContext>();
if (db.Database.CanConnect())
{
try
@ -75,6 +80,8 @@ namespace Oqtane.Infrastructure
{
var result = new Installation { Success = false, Message = string.Empty };
ValidateConfiguration();
// get configuration
if (install == null)
{
@ -87,6 +94,12 @@ namespace Oqtane.Infrastructure
IsNewTenant = false
};
// on upgrade install the associated Nuget package
if (!string.IsNullOrEmpty(install.ConnectionString) && Type.GetType(install.DatabaseType) == null)
{
InstallDatabase(install);
}
var installation = IsInstalled();
if (!installation.Success)
{
@ -173,6 +186,61 @@ namespace Oqtane.Infrastructure
return result;
}
private Installation InstallDatabase(InstallConfig install)
{
var result = new Installation {Success = false, Message = string.Empty};
try
{
var databaseType = install.DatabaseType;
//Get database Type
var type = Type.GetType(databaseType);
//Deploy the database components (if necessary)
if (type == null)
{
//Rename bak extension
var packageFolderName = "Packages";
var path = _environment.ContentRootPath;
var packagesFolder = new DirectoryInfo(Path.Combine(path, packageFolderName));
// iterate through Nuget packages in source folder
foreach (var package in packagesFolder.GetFiles("*.nupkg.bak"))
{
if (package.Name.StartsWith(Utilities.GetAssemblyName(install.DatabaseType)))
{
//rename file
var packageName = Path.Combine(package.DirectoryName, package.Name);
packageName = packageName.Substring(0, packageName.IndexOf(".bak"));
package.MoveTo(packageName, true);
}
}
//Call InstallationManager to install Database Package
using (var scope = _serviceScopeFactory.CreateScope())
{
var installationManager = scope.ServiceProvider.GetRequiredService<IInstallationManager>();
installationManager.InstallPackages();
var assemblyPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
var assembliesFolder = new DirectoryInfo(assemblyPath);
var assemblyFile = new FileInfo($"{assembliesFolder}/{Utilities.GetAssemblyName(install.DatabaseType)}.dll");
AssemblyLoadContext.Default.LoadOqtaneAssembly(assemblyFile);
result.Success = true;
}
}
}
catch (Exception ex)
{
result.Message = ex.Message;
}
return result;
}
private Installation CreateDatabase(InstallConfig install)
{
var result = new Installation { Success = false, Message = string.Empty };
@ -181,22 +249,26 @@ namespace Oqtane.Infrastructure
{
try
{
InstallDatabase(install);
var databaseType = install.DatabaseType;
//Get database Type
var type = Type.GetType(databaseType);
//Create database object from Type
var database = Activator.CreateInstance(type) as IDatabase;
//create data directory if does not exist
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
if (!Directory.Exists(dataDirectory)) Directory.CreateDirectory(dataDirectory ?? String.Empty);
var connectionString = NormalizeConnectionString(install.ConnectionString);
var databaseType = install.DatabaseType;
using (var scope = _serviceScopeFactory.CreateScope())
var dbOptions = new DbContextOptionsBuilder().UseOqtaneDatabase(database, NormalizeConnectionString(install.ConnectionString)).Options;
using (var dbc = new DbContext(dbOptions))
{
var databases = scope.ServiceProvider.GetServices<IOqtaneDatabase>();
using (var dbc = new DbContext(new DbContextOptionsBuilder().UseOqtaneDatabase(databases.Single(d => d.Name == databaseType), connectionString).Options))
{
// create empty database if it does not exist
dbc.Database.EnsureCreated();
result.Success = true;
}
// create empty database if it does not exist
dbc.Database.EnsureCreated();
result.Success = true;
}
}
catch (Exception ex)
@ -220,19 +292,19 @@ namespace Oqtane.Infrastructure
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var databases = scope.ServiceProvider.GetServices<IOqtaneDatabase>();
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
var installation = IsInstalled();
try
{
var dbConfig = new DbConfig(null, null, databases) {ConnectionString = install.ConnectionString, DatabaseType = install.DatabaseType};
UpdateConnectionString(install.ConnectionString);
UpdateDatabaseType(install.DatabaseType);
using (var masterDbContext = new MasterDBContext(new DbContextOptions<MasterDBContext>(), dbConfig))
using (var masterDbContext = new MasterDBContext(new DbContextOptions<MasterDBContext>(), null, _config))
{
var installation = IsInstalled();
if (installation.Success && (install.DatabaseType == "SqlServer" || install.DatabaseType == "LocalDB"))
if (installation.Success && (install.DatabaseType == Constants.DefaultDBType))
{
UpgradeSqlServer(sql, install.ConnectionString, true);
UpgradeSqlServer(sql, install.ConnectionString, install.DatabaseType, true);
}
// Push latest model into database
masterDbContext.Database.Migrate();
@ -243,12 +315,6 @@ namespace Oqtane.Infrastructure
{
result.Message = ex.Message;
}
if (result.Success)
{
UpdateConnectionString(install.ConnectionString);
UpdateDatabaseType(install.DatabaseType);
}
}
}
else
@ -265,11 +331,7 @@ namespace Oqtane.Infrastructure
if (!string.IsNullOrEmpty(install.TenantName) && !string.IsNullOrEmpty(install.Aliases))
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var databases = scope.ServiceProvider.GetServices<IOqtaneDatabase>();
using (var db = GetInstallationContext(databases))
using (var db = GetInstallationContext())
{
Tenant tenant;
if (install.IsNewTenant)
@ -315,7 +377,6 @@ namespace Oqtane.Infrastructure
_cache.Remove("aliases");
}
}
}
result.Success = true;
@ -332,21 +393,21 @@ namespace Oqtane.Infrastructure
using (var scope = _serviceScopeFactory.CreateScope())
{
var upgrades = scope.ServiceProvider.GetRequiredService<IUpgradeManager>();
var databases = scope.ServiceProvider.GetServices<IOqtaneDatabase>();
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
using (var db = GetInstallationContext(databases))
using (var db = GetInstallationContext())
{
foreach (var tenant in db.Tenant.ToList())
{
tenantManager.SetTenant(tenant.TenantId);
try
{
var dbConfig = new DbConfig(null, null, databases) {ConnectionString = tenant.DBConnectionString, DatabaseType = tenant.DBType};
using (var tenantDbContext = new TenantDBContext(dbConfig, null))
using (var tenantDbContext = new TenantDBContext(tenantManager, null))
{
if (install.DatabaseType == "SqlServer" || install.DatabaseType == "LocalDB")
if (install.DatabaseType == Constants.DefaultDBType)
{
UpgradeSqlServer(sql, tenant.DBConnectionString, false);
UpgradeSqlServer(sql, tenant.DBConnectionString, tenant.DBType, false);
}
// Push latest model into database
@ -393,7 +454,7 @@ namespace Oqtane.Infrastructure
{
var moduleDefinitions = scope.ServiceProvider.GetRequiredService<IModuleDefinitionRepository>();
var sql = scope.ServiceProvider.GetRequiredService<ISqlRepository>();
var databases = scope.ServiceProvider.GetServices<IOqtaneDatabase>();
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
foreach (var moduleDefinition in moduleDefinitions.GetModuleDefinitions())
{
@ -403,7 +464,7 @@ namespace Oqtane.Infrastructure
if (moduleType != null)
{
var versions = moduleDefinition.ReleaseVersions.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
using (var db = GetInstallationContext(databases))
using (var db = GetInstallationContext())
{
foreach (var tenant in db.Tenant.ToList())
{
@ -414,13 +475,13 @@ namespace Oqtane.Infrastructure
}
if (index != (versions.Length - 1))
{
if (index == -1) index = 0;
for (var i = index; i < versions.Length; i++)
for (var i = (index + 1); i < versions.Length; i++)
{
try
{
if (moduleType.GetInterface("IInstallable") != null)
{
tenantManager.SetTenant(tenant.TenantId);
var moduleObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, moduleType) as IInstallable;
moduleObject?.Install(tenant, versions[i]);
}
@ -464,12 +525,12 @@ namespace Oqtane.Infrastructure
{
using (var scope = _serviceScopeFactory.CreateScope())
{
// use the SiteState to set the Alias explicitly so the tenant can be resolved
// set the alias explicitly so the tenant can be resolved
var aliases = scope.ServiceProvider.GetRequiredService<IAliasRepository>();
var firstAlias = install.Aliases.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0];
var alias = aliases.GetAliases().FirstOrDefault(item => item.Name == firstAlias);
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
siteState.Alias = alias;
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
tenantManager.SetAlias(alias);
var sites = scope.ServiceProvider.GetRequiredService<ISiteRepository>();
var site = sites.GetSites().FirstOrDefault(item => item.Name == install.SiteName);
@ -490,8 +551,9 @@ namespace Oqtane.Infrastructure
TenantId = tenant.TenantId,
Name = install.SiteName,
LogoFileId = null,
DefaultThemeType = install.DefaultTheme,
DefaultContainerType = install.DefaultContainer,
DefaultThemeType = (!string.IsNullOrEmpty(install.DefaultTheme)) ? install.DefaultTheme : Constants.DefaultTheme,
DefaultContainerType = (!string.IsNullOrEmpty(install.DefaultContainer)) ? install.DefaultContainer : Constants.DefaultContainer,
AdminContainerType = (!string.IsNullOrEmpty(install.DefaultAdminContainer)) ? install.DefaultAdminContainer : Constants.DefaultAdminContainer,
SiteTemplateType = install.SiteTemplate
};
site = sites.AddSite(site);
@ -528,6 +590,7 @@ namespace Oqtane.Infrastructure
SiteId = folder.SiteId,
ParentId = folder.FolderId,
Name = "My Folder",
Type = FolderTypes.Private,
Path = Utilities.PathCombine(folder.Path, user.UserId.ToString(), Path.DirectorySeparatorChar.ToString()),
Order = 1,
IsSystem = true,
@ -565,25 +628,6 @@ namespace Oqtane.Infrastructure
return result;
}
public void AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
{
try
{
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
var json = File.ReadAllText(filePath);
dynamic jsonObj = JsonConvert.DeserializeObject(json);
SetValueRecursively(sectionPathKey, jsonObj, value);
string output = JsonConvert.SerializeObject(jsonObj, Formatting.Indented);
File.WriteAllText(filePath, output);
}
catch (Exception ex)
{
Console.WriteLine("Error writing app settings | {0}", ex);
}
}
private string DenormalizeConnectionString(string connectionString)
{
var dataDirectory = AppDomain.CurrentDomain.GetData("DataDirectory")?.ToString();
@ -591,20 +635,24 @@ namespace Oqtane.Infrastructure
return connectionString;
}
private InstallationContext GetInstallationContext(IEnumerable<IOqtaneDatabase> databases)
private InstallationContext GetInstallationContext()
{
var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
var connectionString = NormalizeConnectionString(_config.GetConnectionString(SettingKeys.ConnectionStringKey));
var databaseType = _config.GetSection(SettingKeys.DatabaseSection)[SettingKeys.DatabaseTypeKey];
return new InstallationContext(databases.Single(d => d.Name == databaseType), connectionString);
IDatabase database = null;
if (!string.IsNullOrEmpty(databaseType))
{
var type = Type.GetType(databaseType);
database = Activator.CreateInstance(type) as IDatabase;
}
return new InstallationContext(database, connectionString);
}
private string GetInstallationConfig(string key, string defaultValue)
{
var value = _config.GetSection(SettingKeys.InstallationSection).GetValue(key, defaultValue);
// double fallback to default value - allow hold sample keys in config
if (string.IsNullOrEmpty(value)) value = defaultValue;
return value;
return _configManager.GetSetting(SettingKeys.InstallationSection, key, defaultValue);
}
private string NormalizeConnectionString(string connectionString)
@ -614,46 +662,47 @@ namespace Oqtane.Infrastructure
return connectionString;
}
private void SetValueRecursively<T>(string sectionPathKey, dynamic jsonObj, T value)
{
// split the string at the first ':' character
var remainingSections = sectionPathKey.Split(":", 2);
var currentSection = remainingSections[0];
if (remainingSections.Length > 1)
{
// continue with the process, moving down the tree
var nextSection = remainingSections[1];
SetValueRecursively(nextSection, jsonObj[currentSection], value);
}
else
{
// we've got to the end of the tree, set the value
jsonObj[currentSection] = value;
}
}
public void UpdateConnectionString(string connectionString)
{
connectionString = DenormalizeConnectionString(connectionString);
if (_config.GetConnectionString(SettingKeys.ConnectionStringKey) != connectionString)
{
AddOrUpdateAppSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", connectionString);
_config.Reload();
_configManager.AddOrUpdateSetting($"{SettingKeys.ConnectionStringsSection}:{SettingKeys.ConnectionStringKey}", connectionString, true);
}
}
public void UpdateDatabaseType(string databaseType)
{
AddOrUpdateAppSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType);
_config.Reload();
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", databaseType, true);
}
public void UpgradeSqlServer(ISqlRepository sql, string connectionString, bool isMaster)
public void UpgradeSqlServer(ISqlRepository sql, string connectionString, string databaseType, bool isMaster)
{
var script = (isMaster) ? "MigrateMaster.sql" : "MigrateTenant.sql";
sql.ExecuteScript(connectionString, Assembly.GetExecutingAssembly(), script);
var query = sql.GetScriptFromAssembly(Assembly.GetExecutingAssembly(), script);
query = query.Replace("{{Version}}", Constants.Version);
sql.ExecuteNonQuery(connectionString, databaseType, query);
}
private void ValidateConfiguration()
{
if (_configManager.GetSetting(SettingKeys.DatabaseSection, SettingKeys.DatabaseTypeKey, "") == "")
{
_configManager.AddOrUpdateSetting($"{SettingKeys.DatabaseSection}:{SettingKeys.DatabaseTypeKey}", Constants.DefaultDBType, true);
}
if (!_configManager.GetSection(SettingKeys.AvailableDatabasesSection).Exists())
{
string databases = "[";
databases += "{ \"Name\": \"LocalDB\", \"ControlType\": \"Oqtane.Installer.Controls.LocalDBConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" },";
databases += "{ \"Name\": \"SQL Server\", \"ControlType\": \"Oqtane.Installer.Controls.SqlServerConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.SqlServer.SqlServerDatabase, Oqtane.Database.SqlServer\" },";
databases += "{ \"Name\": \"SQLite\", \"ControlType\": \"Oqtane.Installer.Controls.SqliteConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Database.Sqlite\" },";
databases += "{ \"Name\": \"MySQL\", \"ControlType\": \"Oqtane.Installer.Controls.MySQLConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.MySQL.SqlServerDatabase, Oqtane.Database.MySQL\" },";
databases += "{ \"Name\": \"PostgreSQL\", \"ControlType\": \"Oqtane.Installer.Controls.PostgreSQLConfig, Oqtane.Client\", \"DBTYpe\": \"Oqtane.Database.PostgreSQL.PostgreSQLDatabase, Oqtane.Database.PostgreSQL\" }";
databases += "]";
_configManager.AddOrUpdateSetting(SettingKeys.AvailableDatabasesSection, JsonConvert.DeserializeObject<dynamic>(databases), true);
}
}
}
}

View File

@ -3,11 +3,13 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Xml;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Hosting;
using Oqtane.Shared;
// ReSharper disable AssignNullToNotNullAttribute
@ -18,170 +20,228 @@ namespace Oqtane.Infrastructure
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
private readonly IWebHostEnvironment _environment;
private readonly IMemoryCache _cache;
public InstallationManager(IHostApplicationLifetime hostApplicationLifetime, IWebHostEnvironment environment, IMemoryCache cache)
public InstallationManager(IHostApplicationLifetime hostApplicationLifetime, IWebHostEnvironment environment)
{
_hostApplicationLifetime = hostApplicationLifetime;
_environment = environment;
_cache = cache;
}
public void InstallPackages(string folders)
public void InstallPackages()
{
if (!InstallPackages(folders, _environment.WebRootPath, _environment.ContentRootPath))
if (!InstallPackages(_environment.WebRootPath, _environment.ContentRootPath))
{
// error installing packages
}
}
public static bool InstallPackages(string folders, string webRootPath, string contentRootPath)
public static bool InstallPackages(string webRootPath, string contentRootPath)
{
bool install = false;
string binFolder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
string binPath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
foreach (string folder in folders.Split(','))
string sourceFolder = Path.Combine(contentRootPath, "Packages");
if (!Directory.Exists(sourceFolder))
{
string sourceFolder = Path.Combine(webRootPath, folder);
if (!Directory.Exists(sourceFolder))
{
Directory.CreateDirectory(sourceFolder);
}
Directory.CreateDirectory(sourceFolder);
}
// iterate through Nuget packages in source folder
foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg"))
// move packages to secure /Packages folder
foreach (var folder in "Modules,Themes,Packages".Split(","))
{
foreach(var file in Directory.GetFiles(Path.Combine(webRootPath, folder), "*.nupkg*"))
{
// iterate through files
using (ZipArchive archive = ZipFile.OpenRead(packagename))
var destinationFile = Path.Combine(sourceFolder, Path.GetFileName(file));
if (File.Exists(destinationFile))
{
string frameworkversion = "";
// locate nuspec
foreach (ZipArchiveEntry entry in archive.Entries)
File.Delete(destinationFile);
}
if (destinationFile.ToLower().EndsWith(".nupkg.bak"))
{
// leave a copy in the current folder as it is distributed with the core framework
File.Copy(file, destinationFile);
}
else
{
// move to destination
File.Move(file, destinationFile);
}
}
}
// iterate through Nuget packages in source folder
foreach (string packagename in Directory.GetFiles(sourceFolder, "*.nupkg"))
{
// iterate through files
using (ZipArchive archive = ZipFile.OpenRead(packagename))
{
string frameworkversion = "";
// locate nuspec
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.ToLower().EndsWith(".nuspec"))
{
if (entry.FullName.ToLower().EndsWith(".nuspec"))
// open nuspec
XmlTextReader reader = new XmlTextReader(entry.Open());
reader.Namespaces = false; // remove namespace
XmlDocument doc = new XmlDocument();
doc.Load(reader);
// get framework dependency
XmlNode node = doc.SelectSingleNode("/package/metadata/dependencies/dependency[@id='Oqtane.Framework']");
if (node != null)
{
// open nuspec
XmlTextReader reader = new XmlTextReader(entry.Open());
reader.Namespaces = false; // remove namespace
XmlDocument doc = new XmlDocument();
doc.Load(reader);
// get framework dependency
XmlNode node = doc.SelectSingleNode("/package/metadata/dependencies/dependency[@id='Oqtane.Framework']");
if (node != null)
{
frameworkversion = node.Attributes["version"].Value;
}
reader.Close();
}
}
// if compatible with framework version
if (frameworkversion == "" || Version.Parse(Constants.Version).CompareTo(Version.Parse(frameworkversion)) >= 0)
{
List<string> assets = new List<string>();
bool manifest = false;
// module and theme packages must be in form of name.1.0.0.nupkg
string name = Path.GetFileNameWithoutExtension(packagename);
string[] segments = name?.Split('.');
if (segments != null) name = string.Join('.', segments, 0, segments.Length - 3);
// deploy to appropriate locations
foreach (ZipArchiveEntry entry in archive.Entries)
{
string foldername = Path.GetDirectoryName(entry.FullName).Split(Path.DirectorySeparatorChar)[0];
string filename = Path.GetFileName(entry.FullName);
if (!manifest && filename == "assets.json")
{
manifest = true;
}
switch (foldername)
{
case "lib":
filename = Path.Combine(binFolder, filename);
ExtractFile(entry, filename);
assets.Add(filename.Replace(contentRootPath, ""));
break;
case "wwwroot":
filename = Path.Combine(webRootPath, Utilities.PathCombine(entry.FullName.Replace("wwwroot/", "").Split('/')));
ExtractFile(entry, filename);
assets.Add(filename.Replace(contentRootPath, ""));
break;
case "runtimes":
var destSubFolder = Path.GetDirectoryName(entry.FullName);
filename = Path.Combine(binFolder, destSubFolder, filename);
ExtractFile(entry, filename);
assets.Add(filename.Replace(contentRootPath, ""));
break;
}
}
// save dynamic list of assets
if (!manifest && assets.Count != 0)
{
string manifestpath = Path.Combine(webRootPath, folder, name, "assets.json");
if (File.Exists(manifestpath))
{
File.Delete(manifestpath);
}
if (!Directory.Exists(Path.GetDirectoryName(manifestpath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(manifestpath));
}
File.WriteAllText(manifestpath, JsonSerializer.Serialize(assets));
frameworkversion = node.Attributes["version"].Value;
}
reader.Close();
break;
}
}
// remove package
File.Delete(packagename);
install = true;
// if compatible with framework version
if (frameworkversion == "" || Version.Parse(Constants.Version).CompareTo(Version.Parse(frameworkversion)) >= 0)
{
List<string> assets = new List<string>();
bool manifest = false;
string name = Path.GetFileNameWithoutExtension(packagename);
// deploy to appropriate locations
foreach (ZipArchiveEntry entry in archive.Entries)
{
string filename = "";
// evaluate entry root folder
switch (entry.FullName.Split('/')[0])
{
case "lib": // lib/net5.0/...
filename = ExtractFile(entry, binPath, 2);
break;
case "wwwroot": // wwwroot/...
filename = ExtractFile(entry, webRootPath, 1);
break;
case "runtimes": // runtimes/name/...
filename = ExtractFile(entry, binPath, 0);
break;
case "ref": // ref/net5.0/...
filename = ExtractFile(entry, Path.Combine(binPath, "ref"), 2);
break;
}
if (filename != "")
{
// ContentRootPath sometimes produces inconsistent path casing - so can't use string.Replace()
filename = Regex.Replace(filename, Regex.Escape(contentRootPath), "", RegexOptions.IgnoreCase);
assets.Add(filename);
if (!manifest && Path.GetExtension(filename) == ".log")
{
manifest = true;
}
}
}
// save dynamic list of assets
if (!manifest && assets.Count != 0)
{
string manifestpath = Path.Combine(sourceFolder, name + ".log");
if (File.Exists(manifestpath))
{
File.Delete(manifestpath);
}
if (!Directory.Exists(Path.GetDirectoryName(manifestpath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(manifestpath));
}
File.WriteAllText(manifestpath, JsonSerializer.Serialize(assets, new JsonSerializerOptions { WriteIndented = true }));
}
}
}
// remove package
File.Delete(packagename);
install = true;
}
return install;
}
private static void ExtractFile(ZipArchiveEntry entry, string filename)
private static string ExtractFile(ZipArchiveEntry entry, string folder, int ignoreLeadingSegments)
{
if (!Directory.Exists(Path.GetDirectoryName(filename)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
string[] segments = entry.FullName.Split('/'); // ZipArchiveEntries always use unix path separator
string filename = Path.Combine(folder, string.Join(Path.DirectorySeparatorChar, segments, ignoreLeadingSegments, segments.Length - ignoreLeadingSegments));
try
{
if (!Directory.Exists(Path.GetDirectoryName(filename)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filename));
}
entry.ExtractToFile(filename, true);
}
catch
{
// an error occurred extracting the file
filename = "";
}
return filename;
}
public bool UninstallPackage(string PackageName)
{
// get manifest with highest version
string packagename = "";
string[] packages = Directory.GetFiles(Path.Combine(_environment.ContentRootPath, "Packages"), PackageName + "*.log");
if (packages.Length > 0)
{
packagename = packages[packages.Length - 1]; // use highest version
}
if (!string.IsNullOrEmpty(packagename))
{
// use manifest to clean up file resources
List<string> assets = JsonSerializer.Deserialize<List<string>>(File.ReadAllText(packagename));
assets.Reverse();
foreach (string asset in assets)
{
// legacy support for assets that were stored as absolute paths
string filepath = asset.StartsWith("\\") ? Path.Combine(_environment.ContentRootPath, asset.Substring(1)) : asset;
if (File.Exists(filepath))
{
File.Delete(filepath);
if (!Directory.EnumerateFiles(Path.GetDirectoryName(filepath)).Any())
{
Directory.Delete(Path.GetDirectoryName(filepath));
}
}
}
// clean up package asset manifests
foreach(string asset in packages)
{
File.Delete(asset);
}
return true;
}
return false;
}
public void UpgradeFramework()
{
string folder = Path.Combine(_environment.WebRootPath, "Framework");
string folder = Path.Combine(_environment.ContentRootPath, "Packages");
if (Directory.Exists(folder))
{
// get package with highest version and clean up any others
// get package with highest version
string packagename = "";
foreach (string package in Directory.GetFiles(folder, "Oqtane.Framework.*.nupkg"))
string[] packages = Directory.GetFiles(folder, Constants.PackageId + ".*.nupkg");
if (packages.Length > 0)
{
if (packagename != "")
{
File.Delete(packagename);
}
packagename = package;
packagename = packages[packages.Length - 1]; // use highest version
}
if (packagename != "")
{
// verify package version
string packageversion = "";
string packageurl = "";
using (ZipArchive archive = ZipFile.OpenRead(packagename))
{
// locate nuspec
@ -200,6 +260,11 @@ namespace Oqtane.Infrastructure
{
packageversion = node.InnerText;
}
node = doc.SelectSingleNode("/package/metadata/projectUrl");
if (node != null)
{
packageurl = node.InnerText;
}
reader.Close();
break;
}
@ -207,9 +272,20 @@ namespace Oqtane.Infrastructure
}
// ensure package version is greater than or equal to current framework version
if (packageversion != "" && Version.Parse(Constants.Version).CompareTo(Version.Parse(packageversion)) <= 0)
if (packageversion != "" && Version.Parse(Constants.Version).CompareTo(Version.Parse(packageversion)) <= 0 && packageurl != "")
{
FinishUpgrade();
// install Oqtane.Framework and Oqtane.Updater nuget packages
InstallPackages();
// download upgrade zip package
var client = new WebClient();
Uri uri = new Uri(packageurl);
string upgradepackage = Path.Combine(folder, uri.Segments[uri.Segments.Length - 1]);
client.DownloadFile(packageurl, upgradepackage);
// install Oqtane.Upgrade zip package
if (File.Exists(upgradepackage))
{
FinishUpgrade();
}
}
}
}
@ -217,19 +293,19 @@ namespace Oqtane.Infrastructure
private void FinishUpgrade()
{
// check if upgrade application exists
string Upgrader = "Oqtane.Upgrade.dll";
// check if updater application exists
string Updater = Constants.UpdaterPackageId + ".dll";
string folder = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
if (folder == null || !File.Exists(Path.Combine(folder, Upgrader))) return;
if (folder == null || !File.Exists(Path.Combine(folder, Updater))) return;
// run upgrade application
// run updater application
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo
{
WorkingDirectory = folder,
FileName = "dotnet",
Arguments = Path.Combine(folder, Upgrader) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
Arguments = Path.Combine(folder, Updater) + " \"" + _environment.ContentRootPath + "\" \"" + _environment.WebRootPath + "\"",
UseShellExecute = false,
ErrorDialog = false,
CreateNoWindow = true,

View File

@ -0,0 +1,16 @@
using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure
{
public interface IConfigManager
{
public IConfigurationSection GetSection(string sectionKey);
public T GetSetting<T>(string settingKey, T defaultValue);
public T GetSetting<T>(string sectionKey, string settingKey, T defaultValue);
void AddOrUpdateSetting<T>(string key, T value, bool reload);
void AddOrUpdateSetting<T>(string file, string key, T value, bool reload);
void RemoveSetting(string key, bool reload);
void RemoveSetting(string file, string key, bool reload);
void Reload();
}
}

View File

@ -2,7 +2,8 @@ namespace Oqtane.Infrastructure
{
public interface IInstallationManager
{
void InstallPackages(string folders);
void InstallPackages();
bool UninstallPackage(string PackageName);
void UpgradeFramework();
void RestartApplication();
}

View File

@ -0,0 +1,12 @@
using Oqtane.Models;
namespace Oqtane.Infrastructure
{
public interface ITenantManager
{
Alias GetAlias();
Tenant GetTenant();
void SetAlias(Alias alias);
void SetTenant(int tenantId);
}
}

View File

@ -92,11 +92,12 @@ namespace Oqtane.Infrastructure
try
{
var notes = "";
var tenants = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
foreach (var tenant in tenants.GetTenants())
var tenantRepository = scope.ServiceProvider.GetRequiredService<ITenantRepository>();
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
foreach (var tenant in tenantRepository.GetTenants())
{
siteState.Alias = new Alias { TenantId = tenant.TenantId };
// set tenant and execute job
tenantManager.SetTenant(tenant.TenantId);
notes += ExecuteJob(scope.ServiceProvider);
}
log.Notes = notes;

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Options;
using Oqtane.Shared;
@ -9,7 +11,7 @@ namespace Oqtane.Infrastructure
public class LocalizationManager : ILocalizationManager
{
private static readonly string DefaultCulture = Constants.DefaultCulture;
private static readonly string[] SupportedCultures = new[] { DefaultCulture };
private static readonly string[] DefaultSupportedCultures = new[] { DefaultCulture };
private readonly LocalizationOptions _localizationOptions;
@ -19,25 +21,19 @@ namespace Oqtane.Infrastructure
}
public string GetDefaultCulture()
=> string.IsNullOrEmpty(_localizationOptions.DefaultCulture)
=> String.IsNullOrEmpty(_localizationOptions.DefaultCulture)
? DefaultCulture
: _localizationOptions.DefaultCulture;
public string[] GetSupportedCultures()
{
List<string> cultures = new List<string>();
var cultures = new List<string>(DefaultSupportedCultures);
foreach(var file in Directory.EnumerateFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "Oqtane.Client.resources.dll", SearchOption.AllDirectories))
{
cultures.Add(Path.GetFileName(Path.GetDirectoryName(file)));
}
if (cultures.Count == 0)
{
return SupportedCultures;
}
else
{
return cultures.ToArray();
}
return cultures.OrderBy(c => c).ToArray();
}
}
}

View File

@ -14,18 +14,18 @@ namespace Oqtane.Infrastructure
public class LogManager : ILogManager
{
private readonly ILogRepository _logs;
private readonly ITenantResolver _tenantResolver;
private readonly IConfigurationRoot _config;
private readonly IUserPermissions _userPermissions;
private readonly IHttpContextAccessor _accessor;
private readonly Alias _alias;
public LogManager(ILogRepository logs, ITenantResolver tenantResolver, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor)
public LogManager(ILogRepository logs, ITenantManager tenantManager, IConfigurationRoot config, IUserPermissions userPermissions, IHttpContextAccessor accessor)
{
_logs = logs;
_tenantResolver = tenantResolver;
_config = config;
_userPermissions = userPermissions;
_accessor = accessor;
_alias = tenantManager.GetAlias();
}
public void Log(LogLevel level, object @class, LogFunction function, string message, params object[] args)
@ -49,10 +49,9 @@ namespace Oqtane.Infrastructure
if (siteId == -1)
{
log.SiteId = null;
Alias alias = _tenantResolver.GetAlias();
if (alias != null)
if (_alias != null)
{
log.SiteId = alias.SiteId;
log.SiteId = _alias.SiteId;
}
}
else

View File

@ -0,0 +1,41 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
namespace Oqtane.Infrastructure
{
internal class TenantMiddleware
{
private readonly RequestDelegate next;
public TenantMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
// check if framework is installed
var config = context.RequestServices.GetService(typeof(IConfiguration)) as IConfiguration;
if (!string.IsNullOrEmpty(config.GetConnectionString("DefaultConnection")))
{
// get alias
var tenantManager = context.RequestServices.GetService(typeof(ITenantManager)) as ITenantManager;
var alias = tenantManager.GetAlias();
// rewrite path by removing alias path prefix from api and pages requests
if (alias != null && !string.IsNullOrEmpty(alias.Path))
{
string path = context.Request.Path.ToString();
if (path.StartsWith("/" + alias.Path) && (path.Contains("/api/") || path.Contains("/pages/")))
{
context.Request.Path = path.Replace("/" + alias.Path, "");
}
}
}
// continue processing
if (next != null) await next(context);
}
}
}

View File

@ -54,10 +54,10 @@ namespace Oqtane.SiteTemplates
new Permission(PermissionNames.View, RoleNames.Admin, true),
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
}.EncodePermissions(),
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web and mobile applications on ASP.NET Core. It leverages the revolutionary new Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.</p>" +
Content = "<p><a href=\"https://www.oqtane.org\" target=\"_new\">Oqtane</a> is an open source <b>modular application framework</b> that provides advanced functionality for developing web and mobile applications on .NET Core. It leverages the Blazor component model to compose a <b>fully dynamic</b> web development experience which can be hosted either client-side or server-side. Whether you are looking for a platform to <b>accelerate your web development</b> efforts, or simply interested in exploring the anatomy of a large-scale Blazor application, Oqtane provides a solid foundation based on proven enterprise architectural principles.</p>" +
"<p align=\"center\"><a href=\"https://www.oqtane.org\" target=\"_new\"><img class=\"img-fluid\" src=\"oqtane-glow.png\"></a></p><p align=\"center\"><a class=\"btn btn-primary\" href=\"https://www.oqtane.org/Community\" target=\"_new\">Join Our Community</a>&nbsp;&nbsp;<a class=\"btn btn-primary\" href=\"https://github.com/oqtane/oqtane.framework\" target=\"_new\">Clone Our Repo</a></p>" +
"<p><a href=\"https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor\" target=\"_new\">Blazor</a> is an open source and cross-platform web UI framework for building single-page apps using .NET and C# instead of JavaScript. Blazor WebAssembly relies on Wasm, an open web standard that does not require plugins or code transpilation in order to run natively in a web browser. Blazor Server uses SignalR to host your application on a web server and provide a responsive and robust development experience. Blazor applications work in all modern web browsers, including mobile browsers.</p>" +
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">ASP.NET Core 3</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
"<p>Blazor is a feature of <a href=\"https://dotnet.microsoft.com/apps/aspnet\" target=\"_new\">.NET Core</a>, the popular cross platform web development framework from Microsoft that extends the <a href=\"https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet\" target=\"_new\" >.NET developer platform</a> with tools and libraries for building web apps.</p>"
},
new PageTemplateModule { ModuleDefinitionName = "Oqtane.Modules.HtmlText, Oqtane.Client", Title = "MIT License", Pane = "Content",
ModulePermissions = new List<Permission> {

View File

@ -0,0 +1,87 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Http;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
public class TenantManager : ITenantManager
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAliasRepository _aliasRepository;
private readonly ITenantRepository _tenantRepository;
private readonly SiteState _siteState;
public TenantManager(IHttpContextAccessor httpContextAccessor, IAliasRepository aliasRepository, ITenantRepository tenantRepository, SiteState siteState)
{
_httpContextAccessor = httpContextAccessor;
_aliasRepository = aliasRepository;
_tenantRepository = tenantRepository;
_siteState = siteState;
}
public Alias GetAlias()
{
Alias alias = null;
if (_siteState != null && _siteState.Alias != null)
{
alias = _siteState.Alias;
}
else
{
// if there is http context
if (_httpContextAccessor.HttpContext != null)
{
// legacy support for client api requests which would include the alias as a path prefix ( ie. {alias}/api/[controller] )
int aliasId;
string[] segments = _httpContextAccessor.HttpContext.Request.Path.Value.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
if (segments.Length > 1 && (segments[1] == "api" || segments[1] == "pages") && int.TryParse(segments[0], out aliasId))
{
alias = _aliasRepository.GetAliases().ToList().FirstOrDefault(item => item.AliasId == aliasId);
}
// resolve alias based on host name and path
if (alias == null)
{
string name = _httpContextAccessor.HttpContext.Request.Host.Value + _httpContextAccessor.HttpContext.Request.Path;
alias = _aliasRepository.GetAlias(name);
}
// if there is a match save it
if (alias != null)
{
_siteState.Alias = alias;
}
}
}
return alias;
}
public Tenant GetTenant()
{
var alias = GetAlias();
if (alias != null)
{
// return tenant details
return _tenantRepository.GetTenants().ToList().FirstOrDefault(item => item.TenantId == alias.TenantId);
}
return null;
}
public void SetAlias(Alias alias)
{
// background processes can set the alias using the SiteState service
_siteState.Alias = alias;
}
public void SetTenant(int tenantId)
{
// background processes can set the alias using the SiteState service
_siteState.Alias = new Alias { TenantId = tenantId };
}
}
}

View File

@ -1,5 +1,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using Oqtane.Models;
using Oqtane.Repository;
using Oqtane.Shared;
@ -11,15 +13,15 @@ namespace Oqtane.Infrastructure
{
public class UpgradeManager : IUpgradeManager
{
private readonly IAliasRepository _aliases;
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly IWebHostEnvironment _environment;
private readonly IConfigManager _configManager;
public UpgradeManager(IAliasRepository aliases, IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment)
public UpgradeManager(IServiceScopeFactory serviceScopeFactory, IWebHostEnvironment environment, IConfigManager configManager)
{
_aliases = aliases;
_serviceScopeFactory = serviceScopeFactory;
_environment = environment;
_configManager = configManager;
}
public void Upgrade(Tenant tenant, string version)
@ -27,9 +29,9 @@ namespace Oqtane.Infrastructure
// core framework upgrade logic - executed for every tenant
using (var scope = _serviceScopeFactory.CreateScope())
{
// set SiteState based on tenant
var siteState = scope.ServiceProvider.GetRequiredService<SiteState>();
siteState.Alias = new Alias { TenantId = tenant.TenantId };
// set tenant
var tenantManager = scope.ServiceProvider.GetRequiredService<ITenantManager>();
tenantManager.SetTenant(tenant.TenantId);
switch (version)
{
@ -39,6 +41,9 @@ namespace Oqtane.Infrastructure
case "2.0.2":
Upgrade_2_0_2(tenant, scope);
break;
case "2.1.0":
Upgrade_2_1_0(tenant, scope);
break;
}
}
}
@ -111,6 +116,18 @@ namespace Oqtane.Infrastructure
}
}
private void Upgrade_2_1_0(Tenant tenant, IServiceScope scope)
{
if (tenant.Name == TenantNames.Master)
{
_configManager.RemoveSetting("Localization:SupportedCultures", true);
if (_configManager.GetSetting("RenderMode", "") == "")
{
_configManager.AddOrUpdateSetting("RenderMode", "ServerPrerendered", true);
}
}
}
private void CreateSitePages(IServiceScope scope, List<PageTemplate> pageTemplates)
{
if (pageTemplates.Count != 0)

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<AliasEntityBuilder> _primaryKey = new("PK_Alias", x => x.AliasId);
private readonly ForeignKey<AliasEntityBuilder> _tenantForeignKey = new("FK_Alias_Tenant", x => x.TenantId, "Tenant", "TenantId", ReferentialAction.Cascade);
public AliasEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public AliasEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
namespace Oqtane.Migrations.EntityBuilders
@ -10,7 +11,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "AppVersions";
private readonly PrimaryKey<AppVersionsEntityBuilder> _primaryKey = new("PK_AppVersions", x => x.Id);
public AppVersionsEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public AppVersionsEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<AspNetUserClaimsEntityBuilder> _primaryKey = new("PK_AspNetUserClaims", x => x.Id);
private readonly ForeignKey<AspNetUserClaimsEntityBuilder> _aspNetUsersForeignKey = new("FK_AspNetUserClaims_AspNetUsers_UserId", x => x.UserId, "AspNetUsers", "Id", ReferentialAction.Cascade);
public AspNetUserClaimsEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public AspNetUserClaimsEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "AspNetUsers";
private readonly PrimaryKey<AspNetUsersEntityBuilder> _primaryKey = new("PK_AspNetUsers", x => x.Id);
public AspNetUsersEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public AspNetUsersEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable UnusedAutoPropertyAccessor.Global
@ -10,7 +11,7 @@ namespace Oqtane.Migrations.EntityBuilders
{
public abstract class AuditableBaseEntityBuilder<TEntityBuilder> : BaseEntityBuilder<TEntityBuilder> where TEntityBuilder : BaseEntityBuilder<TEntityBuilder>
{
protected AuditableBaseEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base (migrationBuilder, database)
protected AuditableBaseEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base (migrationBuilder, database)
{
}

View File

@ -4,6 +4,7 @@ using System.Linq;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable BuiltInTypeReferenceStyleForMemberAccess
@ -13,14 +14,14 @@ namespace Oqtane.Migrations.EntityBuilders
{
private readonly MigrationBuilder _migrationBuilder;
protected BaseEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database)
protected BaseEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database)
{
_migrationBuilder = migrationBuilder;
ActiveDatabase = database;
ForeignKeys = new List<ForeignKey<TEntityBuilder>>();
}
protected IOqtaneDatabase ActiveDatabase { get; }
protected IDatabase ActiveDatabase { get; }
protected abstract TEntityBuilder BuildTable(ColumnsBuilder table);
@ -182,6 +183,19 @@ namespace Oqtane.Migrations.EntityBuilders
onDelete: foreignKey.OnDeleteAction);
}
public void AddForeignKey(string name)
{
var foreignKey = ForeignKeys.Single(k => k.Name == name);
_migrationBuilder.AddForeignKey(
name: RewriteName(foreignKey.Name),
table: RewriteName(EntityTableName),
column: RewriteName(foreignKey.ColumnName),
principalTable: RewriteName(foreignKey.PrincipalTable),
principalColumn: RewriteName(foreignKey.PrincipalColumn),
onDelete: foreignKey.OnDeleteAction);
}
public void DropForeignKey(ForeignKey<TEntityBuilder> foreignKey)
{
DropForeignKey(RewriteName(foreignKey.Name));
@ -217,7 +231,6 @@ namespace Oqtane.Migrations.EntityBuilders
public void DeleteFromTable(string condition = "")
{
var deleteSql = $"DELETE FROM {RewriteName(EntityTableName)} ";
if(!string.IsNullOrEmpty(condition))
{
deleteSql += $"WHERE {condition}";
@ -227,11 +240,8 @@ namespace Oqtane.Migrations.EntityBuilders
public void UpdateColumn(string columnName, string value, string condition = "")
{
var updateValue = value;
var updateSql = $"UPDATE {RewriteName(EntityTableName)} SET {RewriteName(columnName)} = {value} ";
if(!string.IsNullOrEmpty(condition))
if (!string.IsNullOrEmpty(condition))
{
updateSql += $"WHERE {condition}";
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable UnusedAutoPropertyAccessor.Global
@ -10,7 +11,7 @@ namespace Oqtane.Migrations.EntityBuilders
{
public abstract class DeletableAuditableBaseEntityBuilder<TEntityBuilder> : AuditableBaseEntityBuilder<TEntityBuilder> where TEntityBuilder : BaseEntityBuilder<TEntityBuilder>
{
protected DeletableAuditableBaseEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
protected DeletableAuditableBaseEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable UnusedAutoPropertyAccessor.Global
@ -10,7 +11,7 @@ namespace Oqtane.Migrations.EntityBuilders
{
public abstract class DeletableBaseEntityBuilder<TEntityBuilder> : BaseEntityBuilder<TEntityBuilder> where TEntityBuilder : BaseEntityBuilder<TEntityBuilder>
{
protected DeletableBaseEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
protected DeletableBaseEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
}

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<FileEntityBuilder> _primaryKey = new("PK_File", x => x.FileId);
private readonly ForeignKey<FileEntityBuilder> _folderForeignKey = new("FK_File_Folder", x => x.FolderId, "Folder", "FolderId", ReferentialAction.Cascade);
public FileEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public FileEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<FolderEntityBuilder> _primaryKey = new("PK_Folder", x => x.FolderId);
private readonly ForeignKey<FolderEntityBuilder> _siteForeignKey = new("FK_Folder_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public FolderEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public FolderEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "Job";
private readonly PrimaryKey<JobEntityBuilder> _primaryKey = new("PK_Job", x => x.JobId);
public JobEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public JobEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<JobLogEntityBuilder> _primaryKey = new("PK_JobLog", x => x.JobLogId);
private readonly ForeignKey<JobLogEntityBuilder> _jobLogForeignKey = new("FK_JobLog_Job", x => x.JobId, "Job", "JobId", ReferentialAction.Cascade);
public JobLogEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public JobLogEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<LanguageEntityBuilder> _primaryKey = new("PK_Language", x => x.LanguageId);
private readonly ForeignKey<LanguageEntityBuilder> _siteForeignKey = new("FK_Language_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public LanguageEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public LanguageEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<LogEntityBuilder> _primaryKey = new("PK_Log", x => x.LogId);
private readonly ForeignKey<LogEntityBuilder> _siteForeignKey = new("FK_Log_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public LogEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public LogEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "ModuleDefinition";
private readonly PrimaryKey<ModuleDefinitionsEntityBuilder> _primaryKey = new("PK_ModuleDefinition", x => x.ModuleDefinitionId);
public ModuleDefinitionsEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public ModuleDefinitionsEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<ModuleEntityBuilder> _primaryKey = new("PK_Module", x => x.ModuleId);
private readonly ForeignKey<ModuleEntityBuilder> _siteForeignKey = new("FK_Module_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public ModuleEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public ModuleEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<NotificationEntityBuilder> _primaryKey = new("PK_Notification", x => x.NotificationId);
private readonly ForeignKey<NotificationEntityBuilder> _siteForeignKey = new("FK_Notification_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public NotificationEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public NotificationEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<PageEntityBuilder> _primaryKey = new("PK_Page", x => x.PageId);
private readonly ForeignKey<PageEntityBuilder> _siteForeignKey = new("FK_Page_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public PageEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public PageEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
@ -25,7 +26,7 @@ namespace Oqtane.Migrations.EntityBuilders
{
PageId = AddAutoIncrementColumn(table,"PageId");
SiteId = AddIntegerColumn(table,"SiteId");
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
Path = AddStringColumn(table,"Path", 50);
}
@ -41,7 +42,6 @@ namespace Oqtane.Migrations.EntityBuilders
Order = AddIntegerColumn(table,"Order");
IsNavigation = AddBooleanColumn(table,"IsNavigation");
Url = AddStringColumn(table,"Url", 500, true);
LayoutType = AddStringColumn(table,"LayoutType", 200);
UserId = AddIntegerColumn(table,"UserId", true);
IsPersonalizable = AddBooleanColumn(table,"IsPersonalizable");
DefaultContainerType = AddStringColumn(table,"DefaultContainerType", 200, true);
@ -73,8 +73,6 @@ namespace Oqtane.Migrations.EntityBuilders
public OperationBuilder<AddColumnOperation> Url { get; private set; }
public OperationBuilder<AddColumnOperation> LayoutType { get; private set; }
public OperationBuilder<AddColumnOperation> UserId { get; private set; }
public OperationBuilder<AddColumnOperation> IsPersonalizable { get; private set; }

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -15,7 +16,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly ForeignKey<PageModuleEntityBuilder> _moduleForeignKey = new("FK_PageModule_Module", x => x.ModuleId, "Module", "ModuleId", ReferentialAction.NoAction);
private readonly ForeignKey<PageModuleEntityBuilder> _pageForeignKey = new("FK_PageModule_Page", x => x.PageId, "Page", "PageId", ReferentialAction.Cascade);
public PageModuleEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public PageModuleEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -16,7 +17,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly ForeignKey<PermissionEntityBuilder> _userForeignKey = new("FK_Permission_User", x => x.UserId, "User", "UserId", ReferentialAction.NoAction);
private readonly ForeignKey<PermissionEntityBuilder> _roleForeignKey = new("FK_Permission_Role", x => x.RoleId, "Role", "RoleId", ReferentialAction.NoAction);
public PermissionEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public PermissionEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<ProfileEntityBuilder> _primaryKey = new("PK_Profile", x => x.ProfileId);
private readonly ForeignKey<ProfileEntityBuilder> _siteForeignKey = new("FK_Profile_Sites", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public ProfileEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public ProfileEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -14,7 +15,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly PrimaryKey<RoleEntityBuilder> _primaryKey = new("PK_Role", x => x.RoleId);
private readonly ForeignKey<RoleEntityBuilder> _siteForeignKey = new("FK_Role_Site", x => x.SiteId, "Site", "SiteId", ReferentialAction.Cascade);
public RoleEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public RoleEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "Setting";
private readonly PrimaryKey<SettingEntityBuilder> _primaryKey = new("PK_Setting", x => x.SettingId);
public SettingEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public SettingEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
@ -24,7 +25,7 @@ namespace Oqtane.Migrations.EntityBuilders
SettingId = AddAutoIncrementColumn(table,"SettingId");
EntityName = AddStringColumn(table,"EntityName", 50);
EntityId = AddIntegerColumn(table,"EntityId");
SettingName = AddStringColumn(table,"SettingName", 50);
SettingName = AddStringColumn(table,"SettingName", 200);
SettingValue = AddMaxStringColumn(table,"SettingValue");
AddAuditableColumns(table);

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "Site";
private readonly PrimaryKey<SiteEntityBuilder> _primaryKey = new("PK_Site", x => x.SiteId);
public SiteEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public SiteEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;
@ -27,7 +28,6 @@ namespace Oqtane.Migrations.EntityBuilders
LogoFileId = AddIntegerColumn(table,"LogoFileId", true);
FaviconFileId = AddIntegerColumn(table,"FaviconFileId", true);
DefaultThemeType = AddStringColumn(table,"DefaultThemeType", 200);
DefaultLayoutType = AddStringColumn(table,"DefaultLayoutType", 200);
DefaultContainerType = AddStringColumn(table,"DefaultContainerType", 200);
PwaIsEnabled = AddBooleanColumn(table,"PwaIsEnabled");
PwaAppIconFileId = AddIntegerColumn(table,"PwaAppIconFileId", true);
@ -52,8 +52,6 @@ namespace Oqtane.Migrations.EntityBuilders
public OperationBuilder<AddColumnOperation> DefaultThemeType { get; private set; }
public OperationBuilder<AddColumnOperation> DefaultLayoutType { get; private set; }
public OperationBuilder<AddColumnOperation> DefaultContainerType { get; private set; }
public OperationBuilder<AddColumnOperation> PwaIsEnabled { get; private set; }

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "Tenant";
private readonly PrimaryKey<TenantEntityBuilder> _primaryKey = new("PK_Tenant", x => x.TenantId);
public TenantEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database): base(migrationBuilder, database)
public TenantEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database): base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.EntityBuilders
private const string _entityTableName = "User";
private readonly PrimaryKey<UserEntityBuilder> _primaryKey = new("PK_User", x => x.UserId);
public UserEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public UserEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -1,6 +1,7 @@
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Migrations.Operations.Builders;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
// ReSharper disable MemberCanBePrivate.Global
@ -15,7 +16,7 @@ namespace Oqtane.Migrations.EntityBuilders
private readonly ForeignKey<UserRoleEntityBuilder> _userForeignKey = new("FK_UserRole_User", x => x.UserId, "User", "UserId", ReferentialAction.Cascade);
private readonly ForeignKey<UserRoleEntityBuilder> _roleForeignKey = new("FK_UserRole_Role", x => x.RoleId, "Role", "RoleId", ReferentialAction.NoAction);
public UserRoleEntityBuilder(MigrationBuilder migrationBuilder, IOqtaneDatabase database) : base(migrationBuilder, database)
public UserRoleEntityBuilder(MigrationBuilder migrationBuilder, IDatabase database) : base(migrationBuilder, database)
{
EntityTableName = _entityTableName;
PrimaryKey = _primaryKey;

View File

@ -20,6 +20,14 @@ namespace Oqtane.Migrations
public Expression<Func<TEntityBuilder, object>> Column { get;}
public string ColumnName
{
get
{
var body = Column.Body.ToString();
return body.Substring(body.IndexOf(".") + 1);
}
}
public ReferentialAction OnDeleteAction { get; }
public string PrincipalTable { get; }

View File

@ -0,0 +1,51 @@
using System;
using System.Text;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Oqtane.Models;
using Oqtane.Shared;
namespace Oqtane.Migrations.Framework
{
public static class MigrationUtils
{
public static string BuildInsertScript(HistoryRow row, HistoryRepositoryDependencies dependencies, MigrationHistoryTable historyTable )
{
var sqlGenerationHelper = dependencies.SqlGenerationHelper;
var stringTypeMapping = dependencies.TypeMappingSource.GetMapping(typeof(string));
return new StringBuilder().Append("INSERT INTO ")
.Append(sqlGenerationHelper.DelimitIdentifier(historyTable.TableName, historyTable.TableSchema))
.Append(" (")
.Append(sqlGenerationHelper.DelimitIdentifier(historyTable.MigrationIdColumnName))
.Append(", ")
.Append(sqlGenerationHelper.DelimitIdentifier(historyTable.ProductVersionColumnName))
.Append(", ")
.Append(sqlGenerationHelper.DelimitIdentifier(historyTable.AppliedVersionColumnName))
.Append(", ")
.Append(sqlGenerationHelper.DelimitIdentifier(historyTable.AppliedDateColumnName))
.AppendLine(")")
.Append("VALUES (")
.Append(stringTypeMapping.GenerateSqlLiteral(row.MigrationId))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(row.ProductVersion))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(Constants.Version))
.Append(", ")
.Append(stringTypeMapping.GenerateSqlLiteral(DateTime.Now.ToString("yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'fffffffK")))
.Append(")")
.AppendLine(sqlGenerationHelper.StatementTerminator)
.ToString();
}
// only used in upgrade scenarios for modules that used SQL scripts originally
public static string BuildInsertScript(string MigrationId)
{
var query = "IF NOT EXISTS(SELECT 1 FROM __EFMigrationsHistory WHERE MigrationId = '" + MigrationId + "') ";
query += "INSERT INTO __EFMigrationsHistory(MigrationId, ProductVersion, AppliedDate, AppliedVersion) ";
query += "VALUES('" + MigrationId + "', '5.0.4', SYSDATETIME(), '" + Constants.Version + "')";
return query;
}
}
}

View File

@ -1,20 +1,19 @@
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
namespace Oqtane.Migrations
{
public abstract class MultiDatabaseMigration : Migration
{
private readonly IEnumerable<IOqtaneDatabase> _databases;
protected MultiDatabaseMigration(IEnumerable<IOqtaneDatabase> databases)
protected MultiDatabaseMigration(IDatabase database)
{
_databases = databases;
ActiveDatabase = database;
}
protected IOqtaneDatabase ActiveDatabase => _databases.FirstOrDefault(d => d.Provider == ActiveProvider);
protected IDatabase ActiveDatabase { get; }
protected string RewriteName(string name)
{

View File

@ -6,6 +6,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Oqtane.Databases.Interfaces;
using Oqtane.Interfaces;
using Oqtane.Repository.Databases.Interfaces;
@ -13,7 +14,7 @@ namespace Oqtane.Migrations.Framework
{
public class MultiDatabaseMigrationsAssembly: MigrationsAssembly
{
private readonly IEnumerable<IOqtaneDatabase> _databases;
private readonly IDatabase _database;
public MultiDatabaseMigrationsAssembly(
ICurrentDbContext currentContext,
@ -23,15 +24,15 @@ namespace Oqtane.Migrations.Framework
: base(currentContext, options, idGenerator, logger)
{
var multiDatabaseContext = currentContext.Context as IMultiDatabase;
if (multiDatabaseContext != null) _databases = multiDatabaseContext.Databases;
if (multiDatabaseContext != null) _database = multiDatabaseContext.ActiveDatabase;
}
public override Migration CreateMigration(TypeInfo migrationClass, string activeProvider)
{
var hasCtorWithCacheOptions = migrationClass.GetConstructor(new[] { typeof(IEnumerable<IOqtaneDatabase>) }) != null;
var hasCtorWithCacheOptions = migrationClass.GetConstructor(new[] { typeof(IDatabase) }) != null;
if (hasCtorWithCacheOptions)
{
var migration = (Migration)Activator.CreateInstance(migrationClass.AsType(), _databases);
var migration = (Migration)Activator.CreateInstance(migrationClass.AsType(), _database);
if (migration != null)
{
migration.ActiveProvider = activeProvider;

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.01.00.00.00")]
public class InitializeMaster : MultiDatabaseMigration
{
public InitializeMaster(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public InitializeMaster(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.01.00.01.00")]
public class AddAdditionalIndexesInMaster : MultiDatabaseMigration
{
public AddAdditionalIndexesInMaster(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddAdditionalIndexesInMaster(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.02.01.00.00")]
public class AddIndexesForForeignKeyInMaster : MultiDatabaseMigration
{
public AddIndexesForForeignKeyInMaster(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddIndexesForForeignKeyInMaster(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Master
{
[DbContext(typeof(MasterDBContext))]
[Migration("Master.02.01.00.01")]
public class AddDatabaseTypeColumnToTenant : MultiDatabaseMigration
{
public AddDatabaseTypeColumnToTenant(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddDatabaseTypeColumnToTenant(IDatabase database) : base(database)
{
}
@ -22,9 +21,9 @@ namespace Oqtane.Migrations
tenantEntityBuilder.AddStringColumn("DBType", 200, true);
//Update new column if SqlServer (Other Databases will not have any records yet)
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
tenantEntityBuilder.UpdateColumn("DBType", "'SqlServer'");
tenantEntityBuilder.UpdateColumn("DBType", $"'{ActiveDatabase.TypeName}'");
}
}
}

View File

@ -1,18 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.01.00.00.00")]
public class InitializeTenant : MultiDatabaseMigration
{
public InitializeTenant(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public InitializeTenant(IDatabase database) : base(database)
{
}
@ -22,15 +20,22 @@ namespace Oqtane.Migrations
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
siteEntityBuilder.Create();
//Add Column to Site table (for Sql Server only) we will drop it later for Sql Server only
if (ActiveDatabase.Name == "SqlServer")
{
siteEntityBuilder.AddStringColumn("DefaultLayoutType", 200, true);
}
//Create Page table
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.Create();
pageEntityBuilder.AddIndex("IX_Page", new [] {"SiteId", "Path", "UserId"}, true);
//Add Column to Page table (for Sql Server only) we will drop it later for Sql Server only
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
//Add Columns to Page table (for Sql Server only) we will drop them later for Sql Server only
if (ActiveDatabase.Name == "SqlServer")
{
pageEntityBuilder.AddBooleanColumn("EditMode");
pageEntityBuilder.AddStringColumn("LayoutType", 200, true);
}
//Create Module table

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.01.00.01.00")]
public class AddAdditionalIndexesInTenant : MultiDatabaseMigration
{
public AddAdditionalIndexesInTenant(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddAdditionalIndexesInTenant(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.01.00.01.01")]
public class AddAdditionColumnToNotifications : MultiDatabaseMigration
{
public AddAdditionColumnToNotifications(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddAdditionColumnToNotifications(IDatabase database) : base(database)
{
}

View File

@ -1,24 +1,23 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.01.00.02.01")]
public class DropColumnFromPage : MultiDatabaseMigration
{
public DropColumnFromPage(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public DropColumnFromPage(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
//Drop Column from Page table
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.DropColumn("EditMode");

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.00.01")]
public class AddColumnToProfileAndUpdatePage : MultiDatabaseMigration
{
public AddColumnToProfileAndUpdatePage(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddColumnToProfileAndUpdatePage(IDatabase database) : base(database)
{
}
@ -25,7 +24,7 @@ namespace Oqtane.Migrations
profileEntityBuilder.UpdateColumn("Options", "''");
//Alter Column in Page table for Sql Server
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.DropIndex("IX_Page");
@ -41,7 +40,7 @@ namespace Oqtane.Migrations
profileEntityBuilder.DropColumn("Options");
//Alter Column in Page table
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.DropIndex("IX_Page");

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.01.01")]
public class UpdateIconColumnInPage : MultiDatabaseMigration
{
public UpdateIconColumnInPage(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public UpdateIconColumnInPage(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.01.02")]
public class AddLanguageTable : MultiDatabaseMigration
{
public AddLanguageTable(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddLanguageTable(IDatabase database) : base(database)
{
}

View File

@ -1,17 +1,16 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.01.03")]
public class UpdatePageAndAddColumnToSite : MultiDatabaseMigration
{
public UpdatePageAndAddColumnToSite(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public UpdatePageAndAddColumnToSite(IDatabase database) : base(database)
{
}

View File

@ -1,18 +1,17 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.02.01")]
public class AddSiteGuidToSite : MultiDatabaseMigration
{
public AddSiteGuidToSite(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddSiteGuidToSite(IDatabase database) : base(database)
{
}

View File

@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.02.02")]
public class UpdateDefaultContainerTypeInSitePage : MultiDatabaseMigration
{
public UpdateDefaultContainerTypeInSitePage(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
if (ActiveDatabase.Name == "SqlServer")
{
//Update DefaultContainerType In Site
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
siteEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'");
siteEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client'");
//Update DefaultContainerType in Page
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'");
pageEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client'");
//Update ContainerType in PageModule
var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase);
pageModuleEntityBuilder.UpdateColumn("ContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'", "ContainerType = 'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'");
pageModuleEntityBuilder.UpdateColumn("ContainerType", "'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'", "ContainerType = 'Oqtane.Themes.OqtaneTheme.NoTitle, Oqtane.Client'");
}
}
}
}

View File

@ -0,0 +1,52 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.00.02.03")]
public class DropDefaultLayoutInSite : MultiDatabaseMigration
{
public DropDefaultLayoutInSite(IDatabase database) : base(database)
{
}
protected override void Up(MigrationBuilder migrationBuilder)
{
if (ActiveDatabase.Name == "SqlServer")
{
//Alter Column in Setting table for Sql Server
var settingEntityBuilder = new SettingEntityBuilder(migrationBuilder, ActiveDatabase);
settingEntityBuilder.DropIndex("IX_Setting");
settingEntityBuilder.AlterStringColumn("SettingName", 200);
settingEntityBuilder.AddIndex("IX_Setting", new [] {"EntityName", "EntityId", "SettingName"}, true);
//Drop Column from Site Table
var siteEntityBuilder = new SiteEntityBuilder(migrationBuilder, ActiveDatabase);
siteEntityBuilder.DropColumn("DefaultLayoutType");
//Update DefaultContainerType In Site
siteEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'");
siteEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'");
//Drop Column from Page Table
var pageEntityBuilder = new PageEntityBuilder(migrationBuilder, ActiveDatabase);
pageEntityBuilder.DropColumn("LayoutType");
//Update DefaultContainerType in Page
pageEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'");
pageEntityBuilder.UpdateColumn("DefaultContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "DefaultContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'");
//Update ContainerType in PageModule
var pageModuleEntityBuilder = new PageModuleEntityBuilder(migrationBuilder, ActiveDatabase);
pageModuleEntityBuilder.UpdateColumn("ContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultTitle, Oqtane.Client'");
pageModuleEntityBuilder.UpdateColumn("ContainerType", "'Oqtane.Themes.OqtaneTheme.Container, Oqtane.Client'", "ContainerType = 'Oqtane.Themes.OqtaneTheme.DefaultNoTitle, Oqtane.Client'");
}
}
}
}

View File

@ -1,19 +1,17 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Oqtane.Interfaces;
using Oqtane.Databases.Interfaces;
using Oqtane.Migrations.EntityBuilders;
using Oqtane.Repository;
namespace Oqtane.Migrations
namespace Oqtane.Migrations.Tenant
{
[DbContext(typeof(TenantDBContext))]
[Migration("Tenant.02.01.00.00")]
public class AddAppVersionsTable : MultiDatabaseMigration
{
public AddAppVersionsTable(IEnumerable<IOqtaneDatabase> databases) : base(databases)
public AddAppVersionsTable(IDatabase database) : base(database)
{
}
@ -24,7 +22,7 @@ namespace Oqtane.Migrations
appVersionsEntityBuilder.Create();
//Finish SqlServer Migration from DbUp
if (ActiveDatabase.Name == "SqlServer" || ActiveDatabase.Name == "LocalDB")
if (ActiveDatabase.Name == "SqlServer")
{
//Version 1.0.0
InsertVersion(migrationBuilder, "01.00.00", "Oqtane.Scripts.Master.00.09.00.00.sql");
@ -59,7 +57,7 @@ namespace Oqtane.Migrations
migrationBuilder.Sql($@"
INSERT INTO {appVersions}({version}, {appledDate})
VALUES('02.01.00', '{DateTime.UtcNow:u}')
VALUES('02.01.00', '{DateTime.UtcNow.ToString("yyyy'-'MM'-'dd HH':'mm':'ss")}')
");
}

Some files were not shown because too many files have changed in this diff Show More