/// <summary> /// Returns true if all checks are passed to permit a tenant to see an app. /// </summary> /// <param name="installedApp">Entry from tenant, or null if not in tenant</param> /// <param name="availableApp">Entry from app-library, or null if not in library</param> /// <param name="securityModel">Security method to apply.</param> public bool CanSee(InstalledApplication installedApp, AvailableApplication availableApp, AppSecurityModel securityModel) { Guid systemSolution = new Guid("3e67c1c4-aa65-4a9f-95d2-908a9f3614d1"); if (availableApp?.ApplicationId == systemSolution) { return(false); } bool isAvailable = !string.IsNullOrWhiteSpace(availableApp?.PackageVersion); bool isInstalled = !string.IsNullOrWhiteSpace(installedApp?.SolutionVersion); if (!isInstalled && !isAvailable) { return(false); } switch (securityModel) { case AppSecurityModel.Restricted: return(isInstalled); case AppSecurityModel.Full: return(true); case AppSecurityModel.PerTenant: return(isInstalled || availableApp.HasInstallPermission); default: throw new InvalidOperationException("Unknown application security setting"); } }
/// <summary> /// Returns true if all checks are passed to permit an app to be repaired. /// </summary> /// <param name="installedApp">Entry from tenant, or null if not in tenant</param> /// <param name="availableApp">Entry from app-library, or null if not in library</param> /// <param name="securityModel">Security method to apply.</param> public bool CanRepair(InstalledApplication installedApp, AvailableApplication availableApp, AppSecurityModel securityModel) { bool isAvailable = !string.IsNullOrWhiteSpace(availableApp?.PackageVersion); bool isInstalled = !string.IsNullOrWhiteSpace(installedApp?.SolutionVersion); if (!(isInstalled && isAvailable)) { return(false); } bool possibleToRepair = AppManager.CanRepair(installedApp.SolutionVersion, availableApp.PackageVersion); if (!possibleToRepair) { return(false); } switch (securityModel) { case AppSecurityModel.Restricted: case AppSecurityModel.Full: return(true); case AppSecurityModel.PerTenant: return(availableApp.HasInstallPermission); default: throw new InvalidOperationException("Unknown application security setting"); } }
/// <summary> /// Returns true if all checks are passed to permit an app to be deployed. /// </summary> /// <param name="installedApp">Entry from tenant, or null if not in tenant</param> /// <param name="availableApp">Entry from app-library, or null if not in library</param> /// <param name="securityModel">Security method to apply.</param> public bool CanDeploy(InstalledApplication installedApp, AvailableApplication availableApp, AppSecurityModel securityModel) { bool isAvailable = !string.IsNullOrWhiteSpace(availableApp?.PackageVersion); bool isInstalled = !string.IsNullOrWhiteSpace(installedApp?.SolutionVersion); if (isInstalled) { return(false); } if (!isAvailable) { return(false); } switch (securityModel) { case AppSecurityModel.Restricted: return(false); case AppSecurityModel.Full: return(true); case AppSecurityModel.PerTenant: return(availableApp.HasInstallPermission); default: throw new InvalidOperationException("Unknown application security setting"); } }
/// <summary> /// Returns true if all checks are passed to permit an app to be published. /// </summary> /// <param name="installedApp">Entry from tenant, or null if not in tenant</param> /// <param name="availableApp">Entry from app-library, or null if not in library</param> /// <param name="securityModel">Security method to apply.</param> public bool CanPublish(InstalledApplication installedApp, AvailableApplication availableApp, AppSecurityModel securityModel) { bool isAvailable = !string.IsNullOrWhiteSpace(availableApp?.PackageVersion); bool isInstalled = !string.IsNullOrWhiteSpace(installedApp?.SolutionVersion); if (!isInstalled) { return(false); } if (!isAvailable) { return(true); // todo: add special permission for first-time publish of new apps } bool somethingToPublish = AppManager.CanPublish(installedApp.SolutionVersion, availableApp.PackageVersion); if (!somethingToPublish) { return(false); } switch (securityModel) { case AppSecurityModel.Restricted: return(false); case AppSecurityModel.Full: return(true); case AppSecurityModel.PerTenant: return(availableApp.HasPublishPermission); default: throw new InvalidOperationException("Unknown application security setting"); } }
/// <summary> /// Helper function to load application records and validate if OK to proceed with an operation. /// </summary> /// <param name="applicationId">The application Guid. (Not the package guid)</param> /// <param name="validationFunction">Validation callback.</param> /// <returns>True if we can proceed.</returns> private bool CheckIfPossible(Guid applicationId, Func <InstalledApplication, AvailableApplication, AppSecurityModel, bool> validationFunction) { if (validationFunction == null) { throw new ArgumentNullException(nameof(validationFunction)); } // Get application security setting var security = ApplicationSecuritySettings.Current == null ? AppSecurityModel.Restricted : ApplicationSecuritySettings.Current.AppSecurityModel; // Find application records InstalledApplication installedApp = GetInstalledApplications(applicationId).SingleOrDefault( ); AvailableApplication availableApp = GetAvailableApplications(applicationId).SingleOrDefault( ); bool result = validationFunction(installedApp, availableApp, security); return(result); }
/// <summary> /// Returns true if all checks are passed to permit an app to be exported. /// </summary> /// <param name="installedApp">Entry from tenant, or null if not in tenant</param> /// <param name="availableApp">Entry from app-library, or null if not in library</param> /// <param name="securityModel">Security method to apply.</param> public bool CanExport(InstalledApplication installedApp, AvailableApplication availableApp, AppSecurityModel securityModel) { bool isAvailable = availableApp != null; if (!isAvailable) { return(false); } switch (securityModel) { case AppSecurityModel.Restricted: return(false); case AppSecurityModel.Full: return(true); case AppSecurityModel.PerTenant: return(availableApp.HasPublishPermission); // can probably improve this default: throw new InvalidOperationException("Unknown application security setting"); } }
/// <summary> /// Gets the available applications. /// </summary> /// <param name="applicationId">Optionally filter results to this ID only.</param> /// <returns>A list of the applications available.</returns> public IList <AvailableApplication> GetAvailableApplications(Guid?applicationId = null) { var applications = new List <AvailableApplication>( ); using (var ctx = DatabaseContext.GetContext( )) { using (var command = ctx.CreateCommand( )) { const string sql = @"-- GetAvailableApplications DECLARE @isOfType BIGINT = dbo.fnAliasNsId( 'isOfType', 'core', DEFAULT ) DECLARE @app BIGINT = dbo.fnAliasNsId( 'app', 'core', DEFAULT ) DECLARE @name BIGINT = dbo.fnAliasNsId( 'name', 'core', DEFAULT ) DECLARE @packageForApplication BIGINT = dbo.fnAliasNsId( 'packageForApplication', 'core', DEFAULT ) DECLARE @appVersionString BIGINT = dbo.fnAliasNsId( 'appVersionString', 'core', DEFAULT ) DECLARE @appVerId BIGINT = dbo.fnAliasNsId( 'appVerId', 'core', DEFAULT ) DECLARE @applicationId BIGINT = dbo.fnAliasNsId( 'applicationId', 'core', DEFAULT ) DECLARE @canInstallApplication BIGINT = dbo.fnAliasNsId( 'canInstallApplication', 'core', DEFAULT ) DECLARE @canPublishApplication BIGINT = dbo.fnAliasNsId( 'canPublishApplication', 'core', DEFAULT ) SELECT Application = x.Application, ApplicationEntityId = x.ApplicationId, PackageId = pid.Data, PackageEntityId = p.PackageId, Version = p.Version, ApplicationId = aid.Data, Publisher = p1.Data, PublisherUrl = u.Data, ReleaseDate = c.Data, ISNULL(cp.CanPublish, 0) CanPublish, ISNULL(ci.CanInstall, 0) CanInstall FROM ( SELECT Application = n.Data, ApplicationId = n.EntityId FROM Relationship r JOIN Data_NVarChar n ON r.FromId = n.EntityId AND n.FieldId = @name AND n.TenantId = 0 WHERE r.TypeId = @isOfType AND r.TenantId = 0 AND r.ToId = @app ) x JOIN ( SELECT dt.PackageId, dt.ApplicationId, dt.Version, dt.RowNumber FROM ( SELECT ROW_NUMBER( ) OVER ( PARTITION BY r.ToId ORDER BY CAST( '/' + REPLACE( dbo.fnSanitiseVersion( v.Data ), '.', '/' ) + '/' AS HIERARCHYID ) DESC ) AS 'RowNumber', PackageId = r.FromId, ApplicationId = r.ToId, Version = v.Data FROM Relationship r JOIN Data_NVarChar v ON r.FromId = v.EntityId AND r.TypeId = @packageForApplication AND v.FieldId = @appVersionString AND r.TenantId = v.TenantId WHERE r.TenantId = 0 ) dt ) p ON x.ApplicationId = p.ApplicationId JOIN Data_Guid pid ON p.PackageId = pid.EntityId AND pid.TenantId = 0 AND pid.FieldId = @appVerId JOIN Data_Guid aid ON x.ApplicationId = aid.EntityId AND aid.TenantId = 0 AND aid.FieldId = @applicationId LEFT JOIN ( SELECT p.EntityId, p.Data FROM Data_NVarChar p JOIN Data_Alias ap ON p.FieldId = ap.EntityId AND ap.TenantId = 0 AND ap.Data = 'publisher' AND ap.Namespace = 'core' WHERE p.TenantId = 0 ) p1 ON x.ApplicationId = p1.EntityId LEFT JOIN ( SELECT u.EntityId, u.Data FROM Data_NVarChar u JOIN Data_Alias au ON u.FieldId = au.EntityId AND au.TenantId = 0 AND au.Data = 'publisherUrl' AND au.Namespace = 'core' WHERE u.TenantId = 0 ) u ON x.ApplicationId = u.EntityId LEFT JOIN ( SELECT c.EntityId, c.Data FROM Data_DateTime c JOIN Data_Alias ac ON c.FieldId = ac.EntityId AND ac.TenantId = 0 AND ac.Data = 'releaseDate' AND ac.Namespace = 'core' WHERE c.TenantId = 0 ) c ON x.ApplicationId = c.EntityId LEFT JOIN ( SELECT ToId AppId, 1 CanPublish FROM Relationship r WHERE TypeId = @canPublishApplication AND TenantId = 0 AND FromId = @tenantId ) cp ON x.ApplicationId = cp.AppId LEFT JOIN ( SELECT ToId AppId, 1 CanInstall FROM Relationship r WHERE TypeId = @canInstallApplication AND TenantId = 0 AND FromId = @tenantId ) ci ON x.ApplicationId = ci.AppId WHERE p.RowNumber = 1 AND ( @applicationGuid = convert(uniqueidentifier, '00000000-0000-0000-0000-000000000000') OR @applicationGuid = aid.Data )" ; command.CommandText = sql; ctx.AddParameter(command, "@tenantId", DbType.Int64, RequestContext.TenantId); ctx.AddParameter(command, "@applicationGuid", DbType.Guid, applicationId ?? Guid.Empty); using (IDataReader reader = command.ExecuteReader( )) { while (reader.Read( )) { var application = new AvailableApplication { Name = reader.GetString(0), ApplicationEntityId = reader.GetInt64(1), ApplicationVersionId = reader.GetGuid(2), PackageEntityId = reader.GetInt64(3), PackageVersion = reader.GetString(4), ApplicationId = reader.GetGuid(5), Publisher = reader.GetString(6, null), PublisherUrl = reader.GetString(7, null), ReleaseDate = reader.GetDateTime(8, DateTime.MinValue), HasPublishPermission = reader.GetInt32(9) == 1, HasInstallPermission = reader.GetInt32(10) == 1 }; applications.Add(application); } } } } return(applications.GroupBy(a => a.ApplicationId).Select(a => a.OrderByDescending(x => x.PackageEntityId).First( )).ToList( )); }