예제 #1
0
파일: AppHost.cs 프로젝트: BillHertzing/Ace
        /// <summary>
        /// Application specific configuration
        /// This method should initialize any IoC resources utilized by your web service classes.
        /// </summary>
        public override void Configure(Container container)
        {
            // inject the concrete logging implementation
            Log.Debug($"Entering AppHost.Configure method, container is {container.ToString()}");

            // AppSettings is a first class object on the Container, so it will be auto-wired
            // In any other assembly, AppSettings is read-only, so it must be populated in this assembly
            // Location of the configuration files will depend on running as LifeCycle Production/QA/Dev as well as Debug and Release settings

            //It would be nice if ServiceStack implemented the User Secrets pattern that ASP Core provides
            // The Environment variable may define the location of a text file to add to the AppSettings
            string indirectSettingsTextFilepath;

            try {
                indirectSettingsTextFilepath = Environment.GetEnvironmentVariable(agentEnvironmentIndirectSettingsTextFileNameKey);
            }
            catch (SecurityException e) {
                Log.Error($"{cannotReadEnvironmentVariablesSecurityExceptionMessage}: {e.Message}");
                throw new SecurityException(cannotReadEnvironmentVariablesSecurityExceptionMessage, e);
            }

            // ToDo: If the Environment variable does define a location for another text fle, ensure it can be read
            if (indirectSettingsTextFilepath != null)
            {
                try { }
                catch { }
            }
            // Create the AppSettingsBuilder, and add command line arguments to it
            var multiAppSettingsBuilder = new MultiAppSettingsBuilder();

            // Highest priority is any command line variable values, so add command line arguments to the AppSettingsBuilder
            // ToDo: .Add??
            // Next in priority are all environment values, so append AddEnvironmentalVariables to the AppSettingsBuilder
            multiAppSettingsBuilder.AddEnvironmentalVariables();
            // Next in priority are contents of any indirect files mentioned in the environment variables (e.g. User Secrets )
            if (indirectSettingsTextFilepath != null)
            {
                multiAppSettingsBuilder.AddTextFile(indirectSettingsTextFilepath);
            }
            // Next in priority are any configuration settings in a text file named on the command line.
            // ToDo: if a configuration settings text file is specified on the command line, ensure we have permission to read it
            // ToDo: append AddTextFile for configuration settings in a text file specified on the command line to the AppSettingsBuilder
            // Next in priority are any configuration settings in a text file named as a constant string in the app.
            // Location of the text file is relative to the current working directory at the point in time when this method executes.
            // If this file does not exist, it is not considered an error, but if it does exist, not having read permission is an error
            // ToDo: if a configuration settings text file is specified as a constant string in the app, ensure we have permission to read it
            // ToDo: if it exists, append AddTextFile for configuration settings in a text file specified as a constant string in the app to the AppSettingsBuilder
            multiAppSettingsBuilder.AddTextFile(agentSettingsTextFileNameString);
            // Next in priority are any configuration settings in the application config file (AKA AceAgent.exe.config at runtime)
            // Location of the application config file is relative to the current working directory at the point in time when this method executes.
            // If this file does not exist, it is not considered an error, but if it does exist, not having read permission is an error
            // ToDo: if a application config file  is specified as a constant string in the app, ensure we have permission to read it
            // ToDo: if it exists, append AddTextFile for configuration settings in a text file specified as a constant string in the app to the AppSettingsBuilder
            multiAppSettingsBuilder.AddAppSettings()
            // Builtin (compiled in) configuration settings have the lowest priority
            // Add these to the AppSettingsBuilder
            .AddDictionarySettings(DefaultConfiguration.Configuration());
            //Build the AppSettings
            AppSettings = multiAppSettingsBuilder.Build();

            // Create the BaseServices data structure and register it in the container
            //  The AppHost (here, ServiceStack running as a Windows service) has some configuration that is common
            //  to both Frameworks (.Net and .Net Core), which will be setup in a common assembly, so this instance of
            //  the appHost is being passed to the BaseServicesData constructor.
            //  this also registers a BaseServicesData instance in the container
            // ToDo: implement Singleton pattern for BaseServicesData in the DI Container
            var baseServicesData = new BaseServicesData(this);

            container.Register <BaseServicesData>(c => baseServicesData);

            // ToDo: Get the list of plugins to install from the configuration settings, currently hard coded
            // Create the list of PlugIns to load
            var plugInList = new List <IPlugin>()
            {
                //new RealEstateServicesPlugin(),
                //new MinerServicesPlugin(),
                new DiskAnalysisServicesPlugin(),
                new GUIServicesPlugin()
            };

            // Load each plugin here.
            foreach (var pl in plugInList)
            {
                Plugins.Add(pl);
            }

            // ToDo: See Issue #8
            // ToDo place a static, deep-copy of the current application'instance of the configuration settings as the first object in the application's configuration settings history list.

            // start all the timers
            Log.Debug("In AppHost.Configure: starting all timers");
            var timers = Container.Resolve <Dictionary <string, System.Timers.Timer> >();
            var longRunningTasksCheckTimer = timers[Ace.Agent.BaseServices.BaseServicesData.LongRunningTasksCheckTimerName];

            longRunningTasksCheckTimer.Start();

            /*
             * // ToDo: create a NotifyIcon equivalent for a Windows Service or Linux Daemon. Notifiy Icon itself will not work, as that is for WinForms only
             * Log.Debug("in AppHost.Configure: Create a NotifyIcon for AceCommander");
             * Application.EnableVisualStyles();
             * Application.SetCompatibleTextRenderingDefault(false);
             * NotifyIcon notifyIcon1 = new NotifyIcon();
             * ContextMenu contextMenu1 = new ContextMenu();
             * MenuItem menuItem1 = new MenuItem();
             * contextMenu1.MenuItems
             *  .AddRange(new MenuItem[] { menuItem1 });
             * menuItem1.Index=0;
             * menuItem1.Text="E&xit";
             * menuItem1.Click+=new EventHandler(menuItem1_Click);
             * notifyIcon1.Icon=new Icon("atap.ico");
             * notifyIcon1.Text="AceCommander";
             * notifyIcon1.ContextMenu=contextMenu1;
             * notifyIcon1.Visible=true;
             * // Log.Debug("Calling a Web Forms Application instance's static Run method");
             * // Application.Run();
             * // notifyIcon1.Visible = false;
             * Log.Debug("NotifyIcon for AceCommander created");
             */
            Log.Debug("Leaving AppHost.Configure");
        }
예제 #2
0
        /// Configure its PlugInAppSettings and ConfigurationData
        public void BeforePluginsLoaded(IAppHost appHost)
        {
            // Get the IHostEnvironment type object from the ServiceStack Container
            HostEnvironment = appHost.GetContainer().Resolve <Microsoft.Extensions.Hosting.IHostEnvironment>();
            // Determine the environment this PlugIn has been activated in
            string envName = HostEnvironment.EnvironmentName;

            // Populate this PlugIn's AppSettings Configuration Settings and place it in the appSettingsDictionary
            // ToDo: figure out how to place /resolve text files from relative to the location of the PlugIn assembly
            var pluginAppSettingsBuilder = new MultiAppSettingsBuilder();

            // ToDo: command line settings have the highest priority
            // Environment variables have 2nd highest priority
            pluginAppSettingsBuilder.AddEnvironmentalVariables();
            // third priority are Non-Production Configuration settings in a text file
            if (!this.HostEnvironment.IsProduction())
            {
                var settingsTextFileName = Ace.Agent.GUIServices.StringConstants.PluginSettingsTextFileName + '.' + envName + StringConstants.PluginSettingsTextFileSuffix;
                // ToDo: ensure it exists and the ensure we have permission to read it
                // ToDo: Security: There is something called a Time-To-Check / Time-To-Use vulnerability, ensure the way we check then access the text file does not open the program to this vulnerability
                pluginAppSettingsBuilder.AddTextFile(settingsTextFileName);
            }
            // next in priority are Production Configuration settings in a text file
            pluginAppSettingsBuilder.AddTextFile(StringConstants.PluginSettingsTextFileName + StringConstants.PluginSettingsTextFileSuffix);
            // BuiltIn (compiled in) have the lowest priority
            pluginAppSettingsBuilder.AddDictionarySettings(DefaultConfiguration.Production);

            // Populate the PlugInAppSettings property for this PlugIn from the builder
            PlugInAppSettings = pluginAppSettingsBuilder.Build();
            // Populate the ConfigurationData property with an empty configuration data instance
            // populate the GUIS property of the ConfigurationData with a new GUIS having an empty list of GUIs
            ConfigurationData      = new ConfigurationData();
            ConfigurationData.GUIS = new GUIS()
            {
                GUIs = new List <GUI>()
            };
            // Get the GUIMaps POCO from PlugInAppSettings
            // Ensure the PlugInAppSettings has a non-empty ConfigKey for the GUIMaps
            if (!PlugInAppSettings.Exists(StringConstants.GUIMapsConfigKey) ||
                (PlugInAppSettings.GetString(StringConstants.GUIMapsConfigKey) == string.Empty))
            {
                throw new Exception(StringConstants.GUISKeyOrValueNotFoundExceptionMessage);
            }
            // ToDo: Security: this is a place where character strings from external sources are processed, check carefully
            //  any typo in the text file can make the conversion to a POCO break
            GUIMaps gUIMaps;

            try
            {
                gUIMaps = PlugInAppSettings.Get <GUIMaps>(StringConstants.GUIMapsConfigKey);
            }
            catch (Exception)
            {
                //ToDo: Better exception handling
                throw;
            }
            // ToDo: Throw exception if the IEnumerable count = 0
            char[] invalidChars = Path.GetInvalidPathChars();

            // Loop over each GUI defined in pluginAppSettings GUIs, create a virtualFileMapping
            foreach (GUIMap gUIMap in gUIMaps._GUIMaps)
            {
                if (gUIMap.RelativeToContentRootPath.Any(x => invalidChars.Contains(x)))
                {
                    throw new Exception(StringConstants.RelativeRootPathValueContainsIlegalCharacterExceptionMessage);
                }
                // The GenericHost's ContentRootPath is found on the HostEnvironment injected during the .ctor
                var physicalPath = Path.Combine(this.HostEnvironment.ContentRootPath, gUIMap.RelativeToContentRootPath);
                //Log.Debug("in GUIServicesPlugin.Configure, physicalPath = {PhysicalPath}, ContentRootPath = {ContentRootPath} relativeRootPathValue = {RelativeToContentRootPath}", physicalPath, this.HostEnvironment.ContentRootPath, gUIMap.RelativeToContentRootPath);
                //Log.Debug("in GUIServicesPlugin.Configure, index.html exists in physicalpath = {0}", File.Exists(Path.Combine(physicalPath, "index.html")));
                //Log.Debug("in GUIServicesPlugin.Configure, virtualRootPath = {GUIMapVirtualRootPath}", gUIMap.VirtualRootPath);

                // if the VirtualRootPath is empty, setup the webserver's wwwroot to point to the physicalpath
                if (gUIMap.VirtualRootPath == null)
                {
                    Log.Debug("current wwwroot = {Wwwroot}", (appHost as AppHostBase).HostingEnvironment.WebRootPath);
                    // If the webserver's wwwroot is already populated, throw an exception
                    // Set the WebRootPath to the physicalpath
                    // (appHost as AppHostBase).HostingEnvironment.WebRootPath = physicalPath;
                    // Log.Debug("current wwwroot = {Wwwroot}", (appHost as AppHostBase).HostingEnvironment.WebRootPath);
                    appHost.AddVirtualFileSources
                    .Add(new FileSystemMapping("", physicalPath));
                }
                else
                {
                    // Map the virtualRootPath to the physicalpath of the root of the GUI
                    // Wrap in a try catch block in case the physicalRootPath does not exists
                    // ToDo: test for failure condition instead of letting it throw an exception
                    try
                    {
                        appHost.AddVirtualFileSources
                        .Add(new FileSystemMapping(gUIMap.VirtualRootPath, physicalPath));
                    }
                    catch (Exception e)
                    {
                        // ToDo: research how best to log an exception with Serilog, ServiceStack and/or MS
                        // ToDo: USe stringconstant for exception message
                        Log.Debug(e, "in GUIServicesPlugin.Configure, Adding a new VirtualFileSource failed with : {Message}", e.Message);
                        // ToDo wrap in an Aggregate when doing loop
                        throw;
                        // ToDo: figure out how to log this and fallback to something useful
                    }
                }
                // ToDo: Get Version and Description from assembly metadata
                ConfigurationData.GUIS.GUIs.Add(new GUI()
                {
                    Version = "0.0.1", Description = "A sample", GUIMap = gUIMap
                });
            }

            // ToDo: add support for probing for additional GUIS at runtime, add them to ConfigurationData.GUIS.GUIs

            // If a request comes in for the root of the web site, redirect it to the default GUI's index.html file
            // ToDo: Security: this is a place where character strings from external sources are processed, check carefully
            // Get the DefaultGUIVirtualRootPath from PlugInAppSettings
            // Ensure the PlugInAppSettings has a  (possibly empty) ConfigKey for the DefaultGUIVirtualRootPath
            if (!PlugInAppSettings.Exists(StringConstants.DefaultGUIVirtualRootPathConfigKey))
            {
                throw new Exception(StringConstants.DefaultGUIVirtualRootPathKeyOrValueNotFoundExceptionMessage);
            }
            // Ensure the value of the defaultGUIVirtualRootPath matches the value of the VirtualRootPath of one of the GUIMaps
            string defaultGUIVirtualRootPath = PlugInAppSettings.Get <string>(StringConstants.DefaultGUIVirtualRootPathConfigKey);
            GUI    defaultGUI;

            try {
                defaultGUI = ConfigurationData.GUIS.GUIs.Single(gUI => gUI.GUIMap.VirtualRootPath == defaultGUIVirtualRootPath);
            } catch {
                throw new Exception(StringConstants.DefaultGUIVirtualRootPathDoesNotMatchExactlyOneGUIVirtualRootPathExceptionMessage);
            }

            // Set the default redirect. If defaultGUIVirtualRootPath is null, ensure there is not a double // in the  path
            appHost.Config.DefaultRedirectPath = String.Format(StringConstants.DefaultRedirectPathTemplate, defaultGUI.GUIMap.VirtualRootPath);


            // Blazor requires the delivery of static files ending in certain file suffixes.
            // SS disallows some of these by default, so here we tell SS to allow certain file suffixes
            appHost.Config.AllowFileExtensions.Add("dll");
            appHost.Config.AllowFileExtensions.Add("json");
            appHost.Config.AllowFileExtensions.Add("pdb");

            // Blazor requires CORS support, enable the ServiceStack CORS feature
            appHost.Plugins.Add(new CorsFeature(
                                    allowedMethods: "GET, POST, PUT, DELETE, OPTIONS",
                                    allowedOrigins: "*",
                                    allowCredentials: true,
                                    allowedHeaders: "content-type, Authorization, Accept"));

            // create the PlugIn's data object
            GUIServicesData gUIServicesData = new GUIServicesData(PlugInAppSettings, ConfigurationData);

            // Pass the Plugin's data structure to the container so it will be available to every other module and services
            appHost.GetContainer().Register <GUIServicesData>(d => gUIServicesData);

            // ToDo: enable the mechanisms that monitors each plugin-specific data sensor, and start them running
            // ToDo: Need to figure out if / how a PlugIn can add a PlugIn to the parent AppHost, if it detects there is a dependency
        }