/// <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 installed applications. /// </summary> /// <param name="applicationId">Optionally filter results to this ID only.</param> /// <returns></returns> public IList <InstalledApplication> GetInstalledApplications(Guid?applicationId = null) { var applications = new List <InstalledApplication>( ); using (DatabaseContext ctx = DatabaseContext.GetContext( )) { using (IDbCommand command = ctx.CreateCommand( )) { const string sql = @"-- GetInstalledApplications DECLARE @name BIGINT = dbo.fnAliasNsId( 'name', 'core', DEFAULT ) DECLARE @appVersionString BIGINT = dbo.fnAliasNsId( 'appVersionString', 'core', DEFAULT ) DECLARE @appVerId BIGINT = dbo.fnAliasNsId( 'appVerId', 'core', DEFAULT ) DECLARE @packageForApplication BIGINT = dbo.fnAliasNsId( 'packageForApplication', 'core', DEFAULT ) DECLARE @solutionVersionString BIGINT = dbo.fnAliasNsId( 'solutionVersionString', 'core', @tenantId ) DECLARE @isOfType BIGINT = dbo.fnAliasNsId( 'isOfType', 'core', @tenantId ) DECLARE @solution BIGINT = dbo.fnAliasNsId( 'solution', 'core', @tenantId ) SELECT Solution = sn.Data, SolutionEntityId = s.FromId, SolutionVersion = sv.Data, PackageId = pid.Data, PackageEntityId = p.EntityId, Version = pv.Data, ApplicationEntityId = pa.ToId, ApplicationId = aid.UpgradeId, Publisher = p1.Data, PublisherUrl = u.Data, ReleaseDate = c.Data FROM Relationship s -- isOfType solution JOIN Data_NVarChar sv ON s.FromId = sv.EntityId AND sv.FieldId = @solutionVersionString AND sv.TenantId = @tenantId CROSS APPLY dbo.tblFnFieldNVarCharA( s.FromId, s.TenantId, 'name', 'core' ) sn CROSS APPLY dbo.tblFnFieldGuidA( s.FromId, s.TenantId, 'packageId', 'core' ) pid LEFT JOIN Data_Guid p ON pid.Data = p.Data AND p.FieldId = @appVerId AND p.TenantId = 0 LEFT JOIN Relationship pa ON p.EntityId = pa.FromId AND pa.TypeId = @packageForApplication AND pa.TenantId = 0 LEFT JOIN Data_NVarChar pv ON p.EntityId = pv.EntityId AND pv.FieldId = @appVersionString AND pv.TenantId = 0 OUTER APPLY dbo.tblFnFieldNVarCharA( s.FromId, s.TenantId, 'solutionPublisher', 'core' ) p1 OUTER APPLY dbo.tblFnFieldNVarCharA( s.FromId, s.TenantId, 'solutionPublisherUrl', 'core' ) u OUTER APPLY dbo.tblFnFieldDateTimeA( s.FromId, s.TenantId, 'solutionReleaseDate', 'core' ) c JOIN Entity aid ON s.FromId = aid.Id AND s.TenantId = aid.TenantId WHERE s.TenantId = @tenantId AND s.TypeId = @isOfType AND s.ToId = @solution AND ( p.EntityId = pa.FromId OR p.EntityId IS NULL ) AND ( @applicationGuid = convert(uniqueidentifier, '00000000-0000-0000-0000-000000000000') OR @applicationGuid = aid.UpgradeId ) ORDER BY sn.Data, pv.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 InstalledApplication { Name = reader.GetString(0), SolutionEntityId = reader.GetInt64(1), SolutionVersion = reader.GetString(2), ApplicationVersionId = reader.GetGuid(3), PackageEntityId = reader.GetInt64(4, -1), PackageVersion = reader.GetString(5, null), ApplicationEntityId = reader.GetInt64(6, -1), ApplicationId = reader.GetGuid(7, Guid.Empty), Publisher = reader.GetString(8, null), PublisherUrl = reader.GetString(9, null), ReleaseDate = reader.GetDateTime(10, DateTime.MinValue) }; applications.Add(application); } } } } return(applications.GroupBy(a => a.ApplicationId).Select(a => a.OrderByDescending(x => x.PackageEntityId).First( )).ToList( )); }