Ejemplo n.º 1
0
        private async Task ParseStepsAsync(
            Stream stream,
            RecipeDescriptor descriptor,
            Func <RecipeDescriptor, RecipeStepDescriptor, Task> stepActionAsync)
        {
            var serializer = new JsonSerializer();

            StreamReader   streamReader = new StreamReader(stream);
            JsonTextReader reader       = new JsonTextReader(streamReader);

            // Go to Steps, then iterate.
            while (reader.Read())
            {
                if (reader.Path == "steps" && reader.TokenType == JsonToken.StartArray)
                {
                    int stepId = 0;
                    while (reader.Read() && reader.Depth > 1)
                    {
                        if (reader.Depth == 2)
                        {
                            var child = JToken.Load(reader);
                            await stepActionAsync(descriptor, new RecipeStepDescriptor
                            {
                                Id   = (stepId++).ToString(CultureInfo.InvariantCulture),
                                Name = child.Value <string>("name"),
                                Step = child
                            });
                        }
                    }
                }
            }
        }
Ejemplo n.º 2
0
        public Task ShouldTrimValidScriptExpression(string recipeName, string expected)
        {
            return(CreateShellContext().CreateScope().UsingAsync(async scope =>
            {
                // Arrange
                var shellHostMock = new Mock <IShellHost>();

                shellHostMock.Setup(h => h.GetScopeAsync(It.IsAny <ShellSettings>()))
                .Returns(GetScopeAsync);

                var recipeEventHandlers = new List <IRecipeEventHandler> {
                    new RecipeEventHandler()
                };
                var loggerMock = new Mock <ILogger <RecipeExecutor> >();
                var recipeExecutor = new RecipeExecutor(
                    shellHostMock.Object,
                    scope.ShellContext.Settings,
                    recipeEventHandlers,
                    loggerMock.Object);

                // Act
                var executionId = Guid.NewGuid().ToString("n");
                var recipeDescriptor = new RecipeDescriptor {
                    RecipeFileInfo = GetRecipeFileInfo(recipeName)
                };
                await recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, new Dictionary <string, object>(), CancellationToken.None);

                // Assert
                var recipeStep = (recipeEventHandlers.Single() as RecipeEventHandler).Context.Step;
                Assert.Equal(expected, recipeStep.SelectToken("data.[0].TitlePart.Title").ToString());
            }));
        }
Ejemplo n.º 3
0
        public async Task ServerDeploymentSourceIsReadableByRecipe()
        {
            // Arrange
            var recipeFile = "Recipe.json";

            var expectedSettings        = CreateSettings("https://deploy.localhost", TokenFormat.JsonWebToken, true);
            var deployServerServiceMock = CreateServerServiceWithSettingsMock(expectedSettings);

            var actualSettings          = CreateSettings("https://recipe.localhost", TokenFormat.DataProtection, false);
            var recipeServerServiceMock = CreateServerServiceWithSettingsMock(actualSettings);

            var settingsProperties = typeof(OpenIdServerSettings)
                                     .GetProperties();

            foreach (var property in settingsProperties)
            {
                Assert.NotEqual(
                    property.GetValue(expectedSettings),
                    property.GetValue(actualSettings));
            }

            var fileBuilder = new MemoryFileBuilder();
            var descriptor  = new RecipeDescriptor();
            var result      = new DeploymentPlanResult(fileBuilder, descriptor);

            var deploymentSource = new OpenIdServerDeploymentSource(deployServerServiceMock.Object);

            // Act
            await deploymentSource.ProcessDeploymentStepAsync(new OpenIdServerDeploymentStep(), result);

            await result.FinalizeAsync();

            var deploy = JObject.Parse(
                fileBuilder.GetFileContents(
                    recipeFile,
                    Encoding.UTF8));

            var recipeContext = new RecipeExecutionContext
            {
                RecipeDescriptor = descriptor,
                Name             = deploy.Property("steps").Value.First.Value <string>("name"),
                Step             = (JObject)deploy.Property("steps").Value.First,
            };

            var recipeStep = new OpenIdServerSettingsStep(recipeServerServiceMock.Object);
            await recipeStep.ExecuteAsync(recipeContext);

            // Assert
            foreach (var property in settingsProperties)
            {
                Assert.Equal(
                    property.GetValue(expectedSettings),
                    property.GetValue(actualSettings));
            }
        }
        public async Task ImportFromFileAsync(IFileProvider fileProvider)
        {
            var executionId      = Guid.NewGuid().ToString("n");
            var recipeDescriptor = new RecipeDescriptor
            {
                FileProvider   = fileProvider,
                BasePath       = "",
                RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json")
            };

            await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, new object(), CancellationToken.None);
        }
Ejemplo n.º 5
0
        public DeploymentPlanResult(IFileBuilder fileBuilder, RecipeDescriptor recipeDescriptor)
        {
            FileBuilder = fileBuilder;

            Recipe                  = new JObject();
            Recipe["name"]          = recipeDescriptor.Name ?? "";
            Recipe["displayName"]   = recipeDescriptor.DisplayName ?? "";
            Recipe["description"]   = recipeDescriptor.Description ?? "";
            Recipe["author"]        = recipeDescriptor.Author ?? "";
            Recipe["website"]       = recipeDescriptor.WebSite ?? "";
            Recipe["version"]       = recipeDescriptor.Version ?? "";
            Recipe["issetuprecipe"] = recipeDescriptor.IsSetupRecipe;
            Recipe["categories"]    = new JArray(recipeDescriptor.Categories ?? new string[] { });
            Recipe["tags"]          = new JArray(recipeDescriptor.Tags ?? new string[] { });
        }
Ejemplo n.º 6
0
        public async Task <IActionResult> Execute(int id)
        {
            if (!await _authorizationService.AuthorizeAsync(User, Permissions.Export))
            {
                return(Forbid());
            }

            var deploymentPlan = await _session.GetAsync <DeploymentPlan>(id);

            if (deploymentPlan == null)
            {
                return(NotFound());
            }

            string archiveFileName;
            var    filename = deploymentPlan.Name.ToSafeName() + ".zip";

            using (var fileBuilder = new TemporaryFileBuilder())
            {
                archiveFileName = fileBuilder.Folder + ".zip";

                var recipeDescriptor         = new RecipeDescriptor();
                var recipeFileDeploymentStep = deploymentPlan.DeploymentSteps.FirstOrDefault(ds => ds.Name == nameof(RecipeFileDeploymentStep)) as RecipeFileDeploymentStep;

                if (recipeFileDeploymentStep != null)
                {
                    recipeDescriptor.Name          = recipeFileDeploymentStep.RecipeName;
                    recipeDescriptor.DisplayName   = recipeFileDeploymentStep.DisplayName;
                    recipeDescriptor.Description   = recipeFileDeploymentStep.Description;
                    recipeDescriptor.Author        = recipeFileDeploymentStep.Author;
                    recipeDescriptor.WebSite       = recipeFileDeploymentStep.WebSite;
                    recipeDescriptor.Version       = recipeFileDeploymentStep.Version;
                    recipeDescriptor.IsSetupRecipe = recipeFileDeploymentStep.IsSetupRecipe;
                    recipeDescriptor.Categories    = (recipeFileDeploymentStep.Categories ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries);
                    recipeDescriptor.Tags          = (recipeFileDeploymentStep.Tags ?? "").Split(',', StringSplitOptions.RemoveEmptyEntries);
                }

                var deploymentPlanResult = new DeploymentPlanResult(fileBuilder, recipeDescriptor);
                await _deploymentManager.ExecuteDeploymentPlanAsync(deploymentPlan, deploymentPlanResult);

                ZipFile.CreateFromDirectory(fileBuilder.Folder, archiveFileName);
            }

            return(new PhysicalFileResult(archiveFileName, "application/zip")
            {
                FileDownloadName = filename
            });
        }
Ejemplo n.º 7
0
        public async Task <IActionResult> Import([FromBody] ArchiveContainer archive)
        {
            var remoteClientList = await _remoteClientService.GetRemoteClientListAsync();

            var remoteClient = remoteClientList.RemoteClients.FirstOrDefault(x => x.ClientName == archive.ClientName);

            if (remoteClient == null || archive.ApiKey != remoteClient.ApiKey || archive.ClientName != remoteClient.ClientName)
            {
                return(StatusCode(403, "The Api Key was not recognized"));
            }

            var tempArchiveName   = Path.GetTempFileName() + ".zip";
            var tempArchiveFolder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());

            try
            {
                byte[] content = Convert.FromBase64String(archive.ArchiveBase64);
                System.IO.File.WriteAllBytes(tempArchiveName, content);

                ZipFile.ExtractToDirectory(tempArchiveName, tempArchiveFolder);

                var executionId      = Guid.NewGuid().ToString("n");
                var fileProvider     = new PhysicalFileProvider(tempArchiveFolder);
                var recipeDescriptor = new RecipeDescriptor
                {
                    FileProvider   = fileProvider,
                    BasePath       = "",
                    RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json")
                };

                await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, new object());
            }
            finally
            {
                if (System.IO.File.Exists(tempArchiveName))
                {
                    System.IO.File.Delete(tempArchiveName);
                }

                if (Directory.Exists(tempArchiveFolder))
                {
                    Directory.Delete(tempArchiveFolder, true);
                }
            }

            return(Ok());
        }
        public async Task ImportFromFileAsync(IFileProvider fileProvider)
        {
            var executionId      = Guid.NewGuid().ToString("n");
            var recipeDescriptor = new RecipeDescriptor
            {
                FileProvider   = fileProvider,
                BasePath       = "",
                RecipeFileInfo = fileProvider.GetFileInfo("Recipe.json")
            };

            var environment = new Dictionary <string, object>();
            await _environmentProviders.OrderBy(x => x.Order).InvokeAsync((provider, env) => provider.PopulateEnvironmentAsync(env), environment, _logger);

            await _recipeExecutor.ExecuteAsync(executionId, recipeDescriptor, environment, CancellationToken.None);

            await _shellHost.ReleaseShellContextAsync(_shellSettings);
        }
Ejemplo n.º 9
0
        public async Task <string> ExecuteAsync(string executionId, RecipeDescriptor recipeDescriptor)
        {
            await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutingAsync(executionId, recipeDescriptor));

            try
            {
                var parsersForFileExtension = _recipeOptions
                                              .RecipeFileExtensions
                                              .Where(rfx => Path.GetExtension(rfx.Key) == Path.GetExtension(recipeDescriptor.RecipeFileInfo.PhysicalPath));

                using (var stream = recipeDescriptor.RecipeFileInfo.CreateReadStream())
                {
                    RecipeResult result = new RecipeResult {
                        ExecutionId = executionId
                    };
                    List <RecipeStepResult> stepResults = new List <RecipeStepResult>();

                    foreach (var parserForFileExtension in parsersForFileExtension)
                    {
                        var recipeParser = _recipeParsers.First(x => x.GetType() == parserForFileExtension.Value);

                        await recipeParser.ProcessRecipeAsync(stream, (recipe, recipeStep) =>
                        {
                            // TODO, create Result prior to run
                            stepResults.Add(new RecipeStepResult
                            {
                                ExecutionId = executionId,
                                RecipeName  = recipeDescriptor.Name,
                                StepId      = recipeStep.Id,
                                StepName    = recipeStep.Name
                            });
                            return(Task.CompletedTask);
                        });
                    }

                    result.Steps = stepResults;
                    await _recipeStore.UpdateAsync(result);
                }

                using (var stream = recipeDescriptor.RecipeFileInfo.CreateReadStream())
                {
                    foreach (var parserForFileExtension in parsersForFileExtension)
                    {
                        var recipeParser = _recipeParsers.First(x => x.GetType() == parserForFileExtension.Value);

                        await recipeParser.ProcessRecipeAsync(stream, async (recipe, recipeStep) =>
                        {
                            var shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings);
                            using (var scope = shellContext.CreateServiceScope())
                            {
                                if (!shellContext.IsActivated)
                                {
                                    var eventBus = scope.ServiceProvider.GetService <IEventBus>();
                                    await eventBus.NotifyAsync <IOrchardShellEvents>(x => x.ActivatingAsync());
                                    await eventBus.NotifyAsync <IOrchardShellEvents>(x => x.ActivatedAsync());

                                    shellContext.IsActivated = true;
                                }

                                var recipeStepExecutor = scope.ServiceProvider.GetRequiredService <IRecipeStepExecutor>();

                                if (_applicationLifetime.ApplicationStopping.IsCancellationRequested)
                                {
                                    throw new OrchardException(T["Recipe cancelled, application is restarting"]);
                                }

                                await recipeStepExecutor.ExecuteAsync(executionId, recipeStep);
                            }

                            // The recipe execution might have invalidated the shell by enabling new features,
                            // so the deferred tasks need to run on an updated shell context if necessary.
                            shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings);
                            using (var scope = shellContext.CreateServiceScope())
                            {
                                var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>();

                                // The recipe might have added some deferred tasks to process
                                if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks)
                                {
                                    var taskContext = new DeferredTaskContext(scope.ServiceProvider);
                                    await deferredTaskEngine.ExecuteTasksAsync(taskContext);
                                }
                            }
                        });
                    }
                }

                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutedAsync(executionId, recipeDescriptor));

                return(executionId);
            }
            catch (Exception)
            {
                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.ExecutionFailedAsync(executionId, recipeDescriptor));

                throw;
            }
        }
Ejemplo n.º 10
0
        public async Task <ActionResult> IndexPOST(SetupViewModel model)
        {
            if (!string.IsNullOrWhiteSpace(_shellSettings["Secret"]))
            {
                if (string.IsNullOrEmpty(model.Secret) || !await IsTokenValid(model.Secret))
                {
                    _logger.LogWarning("An attempt to access '{TenantName}' without providing a valid secret was made", _shellSettings.Name);
                    return(StatusCode(404));
                }
            }

            model.DatabaseProviders = _databaseProviders;
            model.Recipes           = await _setupService.GetSetupRecipesAsync();

            var selectedProvider = model.DatabaseProviders.FirstOrDefault(x => x.Value == model.DatabaseProvider);

            if (!model.DatabaseConfigurationPreset)
            {
                if (selectedProvider != null && selectedProvider.HasConnectionString && String.IsNullOrWhiteSpace(model.ConnectionString))
                {
                    ModelState.AddModelError(nameof(model.ConnectionString), T["The connection string is mandatory for this provider."]);
                }
            }

            if (String.IsNullOrEmpty(model.Password))
            {
                ModelState.AddModelError(nameof(model.Password), T["The password is required."]);
            }

            if (model.Password != model.PasswordConfirmation)
            {
                ModelState.AddModelError(nameof(model.PasswordConfirmation), T["The password confirmation doesn't match the password."]);
            }

            RecipeDescriptor selectedRecipe = null;

            if (!string.IsNullOrEmpty(_shellSettings["RecipeName"]))
            {
                selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == _shellSettings["RecipeName"]);
                if (selectedRecipe == null)
                {
                    ModelState.AddModelError(nameof(model.RecipeName), T["Invalid recipe."]);
                }
            }
            else if (String.IsNullOrEmpty(model.RecipeName) || (selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == model.RecipeName)) == null)
            {
                ModelState.AddModelError(nameof(model.RecipeName), T["Invalid recipe."]);
            }

            if (!ModelState.IsValid)
            {
                CopyShellSettingsValues(model);
                return(View(model));
            }

            var setupContext = new SetupContext
            {
                ShellSettings   = _shellSettings,
                SiteName        = model.SiteName,
                EnabledFeatures = null, // default list,
                AdminUsername   = model.UserName,
                AdminEmail      = model.Email,
                AdminPassword   = model.Password,
                Errors          = new Dictionary <string, string>(),
                Recipe          = selectedRecipe,
                SiteTimeZone    = model.SiteTimeZone,
            };

            if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"]))
            {
                setupContext.DatabaseProvider         = _shellSettings["DatabaseProvider"];
                setupContext.DatabaseConnectionString = _shellSettings["ConnectionString"];
                setupContext.DatabaseTablePrefix      = _shellSettings["TablePrefix"];
            }
            else
            {
                setupContext.DatabaseProvider         = model.DatabaseProvider;
                setupContext.DatabaseConnectionString = model.ConnectionString;
                setupContext.DatabaseTablePrefix      = model.TablePrefix;
            }

            var executionId = await _setupService.SetupAsync(setupContext);

            // Check if a component in the Setup failed
            if (setupContext.Errors.Any())
            {
                foreach (var error in setupContext.Errors)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }

                return(View(model));
            }

            return(Redirect("~/"));
        }
Ejemplo n.º 11
0
        public async Task <string> ExecuteAsync(string executionId, RecipeDescriptor recipeDescriptor, object environment)
        {
            await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutingAsync(executionId, recipeDescriptor));

            try
            {
                _environmentMethodProvider = new ParametersMethodProvider(environment);

                var result = new RecipeResult {
                    ExecutionId = executionId
                };

                await _recipeStore.CreateAsync(result);

                using (StreamReader file = File.OpenText(recipeDescriptor.RecipeFileInfo.PhysicalPath))
                {
                    using (var reader = new JsonTextReader(file))
                    {
                        // Go to Steps, then iterate.
                        while (reader.Read())
                        {
                            if (reader.Path == "variables")
                            {
                                reader.Read();

                                var variables = JObject.Load(reader);
                                _variablesMethodProvider = new VariablesMethodProvider(variables);
                            }

                            if (reader.Path == "steps" && reader.TokenType == JsonToken.StartArray)
                            {
                                while (reader.Read() && reader.Depth > 1)
                                {
                                    if (reader.Depth == 2)
                                    {
                                        var child = JObject.Load(reader);

                                        var recipeStep = new RecipeExecutionContext
                                        {
                                            Name        = child.Value <string>("name"),
                                            Step        = child,
                                            ExecutionId = executionId,
                                            Environment = environment
                                        };

                                        var stepResult = new RecipeStepResult {
                                            StepName = recipeStep.Name
                                        };
                                        result.Steps.Add(stepResult);
                                        await _recipeStore.UpdateAsync(result);

                                        ExceptionDispatchInfo capturedException = null;
                                        try
                                        {
                                            await ExecuteStepAsync(recipeStep);

                                            stepResult.IsSuccessful = true;
                                        }
                                        catch (Exception e)
                                        {
                                            stepResult.IsSuccessful = false;
                                            stepResult.ErrorMessage = e.ToString();

                                            // Because we can't do some async processing the in catch or finally
                                            // blocks, we store the exception to throw it later.

                                            capturedException = ExceptionDispatchInfo.Capture(e);
                                        }

                                        stepResult.IsCompleted = true;
                                        await _recipeStore.UpdateAsync(result);

                                        if (stepResult.IsSuccessful == false)
                                        {
                                            capturedException.Throw();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutedAsync(executionId, recipeDescriptor));

                return(executionId);
            }
            catch (Exception)
            {
                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.ExecutionFailedAsync(executionId, recipeDescriptor));

                throw;
            }
        }
Ejemplo n.º 12
0
        public async Task <ActionResult> IndexPOST(SetupViewModel model)
        {
            model.DatabaseProviders = _databaseProviders;
            model.Recipes           = await _setupService.GetSetupRecipesAsync();

            var selectedProvider = model.DatabaseProviders.FirstOrDefault(x => x.Value == model.DatabaseProvider);

            if (selectedProvider != null && selectedProvider.HasConnectionString && String.IsNullOrWhiteSpace(model.ConnectionString))
            {
                ModelState.AddModelError(nameof(model.ConnectionString), T["The connection string is mandatory for this provider."]);
            }

            if (String.IsNullOrEmpty(model.Password))
            {
                ModelState.AddModelError(nameof(model.Password), T["The password is required."]);
            }

            if (model.Password != model.PasswordConfirmation)
            {
                ModelState.AddModelError(nameof(model.PasswordConfirmation), T["The password confirmation doesn't match the password."]);
            }

            RecipeDescriptor selectedRecipe = null;

            if (String.IsNullOrEmpty(model.RecipeName) || (selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == model.RecipeName)) == null)
            {
                ModelState.AddModelError(nameof(model.RecipeName), T["Invalid recipe."]);
            }

            if (!String.IsNullOrEmpty(_shellSettings.ConnectionString))
            {
                model.ConnectionStringPreset = true;
                model.ConnectionString       = _shellSettings.ConnectionString;
            }

            if (!String.IsNullOrEmpty(_shellSettings.DatabaseProvider))
            {
                model.DatabaseProviderPreset = true;
                model.DatabaseProvider       = _shellSettings.DatabaseProvider;
            }

            if (!String.IsNullOrEmpty(_shellSettings.TablePrefix))
            {
                model.TablePrefixPreset = true;
                model.TablePrefix       = _shellSettings.TablePrefix;
            }

            if (!ModelState.IsValid)
            {
                return(View(model));
            }

            var setupContext = new SetupContext
            {
                SiteName        = model.SiteName,
                EnabledFeatures = null, // default list,
                AdminUsername   = model.AdminUserName,
                AdminEmail      = model.AdminEmail,
                AdminPassword   = model.Password,
                Errors          = new Dictionary <string, string>(),
                Recipe          = selectedRecipe
            };

            if (!model.DatabaseProviderPreset)
            {
                setupContext.DatabaseProvider = model.DatabaseProvider;
            }

            if (!model.ConnectionStringPreset)
            {
                setupContext.DatabaseConnectionString = model.ConnectionString;
            }

            if (!model.TablePrefixPreset)
            {
                setupContext.DatabaseTablePrefix = model.TablePrefix;
            }

            var executionId = await _setupService.SetupAsync(setupContext);

            // Check if a component in the Setup failed
            if (setupContext.Errors.Any())
            {
                foreach (var error in setupContext.Errors)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }

                return(View(model));
            }

            var urlPrefix = "";

            if (!String.IsNullOrWhiteSpace(_shellSettings.RequestUrlPrefix))
            {
                urlPrefix = _shellSettings.RequestUrlPrefix + "/";
            }

            return(Redirect("~/" + urlPrefix));
        }
Ejemplo n.º 13
0
 public Task <string> Execute(RecipeDescriptor descriptor, CancellationToken cancellationToken = default)
 {
     throw new System.NotImplementedException();
 }
        public async Task <ActionResult> Setup(SetupApiViewModel model)
        {
            if (!IsDefaultShell())
            {
                return(Unauthorized());
            }

            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants))
            {
                return(Unauthorized());
            }

            if (!ModelState.IsValid)
            {
                return(BadRequest());
            }

            if (!_shellHost.TryGetSettings(model.Name, out var shellSettings))
            {
                ModelState.AddModelError(nameof(SetupApiViewModel.Name), S["Tenant not found: '{0}'", model.Name]);
            }

            if (shellSettings.State == TenantState.Running)
            {
                return(StatusCode(201));
            }

            if (shellSettings.State != TenantState.Uninitialized)
            {
                return(BadRequest(S["The tenant can't be setup."]));
            }

            var selectedProvider = _databaseProviders.FirstOrDefault(x => String.Equals(x.Value, model.DatabaseProvider, StringComparison.OrdinalIgnoreCase));

            if (selectedProvider == null)
            {
                return(BadRequest(S["The database provider is not defined."]));
            }

            var tablePrefix = shellSettings.TablePrefix;

            if (String.IsNullOrEmpty(tablePrefix))
            {
                tablePrefix = model.TablePrefix;
            }

            var connectionString = shellSettings.ConnectionString;

            if (String.IsNullOrEmpty(connectionString))
            {
                connectionString = model.ConnectionString;
            }

            if (selectedProvider.HasConnectionString && String.IsNullOrEmpty(connectionString))
            {
                return(BadRequest(S["The connection string is required for this database provider."]));
            }

            var recipeName = shellSettings.RecipeName;

            if (String.IsNullOrEmpty(recipeName))
            {
                recipeName = model.RecipeName;
            }

            RecipeDescriptor recipeDescriptor = null;

            if (String.IsNullOrEmpty(recipeName))
            {
                if (model.Recipe == null)
                {
                    return(BadRequest(S["Either 'Recipe' or 'RecipeName' is required."]));
                }

                var tempFilename = Path.GetTempFileName();

                using (var fs = System.IO.File.Create(tempFilename))
                {
                    await model.Recipe.CopyToAsync(fs);
                }

                var fileProvider = new PhysicalFileProvider(Path.GetDirectoryName(tempFilename));

                recipeDescriptor = new RecipeDescriptor
                {
                    FileProvider   = fileProvider,
                    BasePath       = "",
                    RecipeFileInfo = fileProvider.GetFileInfo(Path.GetFileName(tempFilename))
                };
            }
            else
            {
                var setupRecipes = await _setupService.GetSetupRecipesAsync();

                recipeDescriptor = setupRecipes.FirstOrDefault(x => String.Equals(x.Name, recipeName, StringComparison.OrdinalIgnoreCase));

                if (recipeDescriptor == null)
                {
                    return(BadRequest(S["Recipe '{0}' not found.", recipeName]));
                }
            }

            var setupContext = new SetupContext
            {
                ShellSettings            = shellSettings,
                SiteName                 = model.SiteName,
                EnabledFeatures          = null, // default list,
                AdminUsername            = model.UserName,
                AdminEmail               = model.Email,
                AdminPassword            = model.Password,
                Errors                   = new Dictionary <string, string>(),
                Recipe                   = recipeDescriptor,
                SiteTimeZone             = model.SiteTimeZone,
                DatabaseProvider         = selectedProvider.Name,
                DatabaseConnectionString = connectionString,
                DatabaseTablePrefix      = tablePrefix
            };

            var executionId = await _setupService.SetupAsync(setupContext);

            // Check if a component in the Setup failed
            if (setupContext.Errors.Any())
            {
                foreach (var error in setupContext.Errors)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }

                return(StatusCode(500, ModelState));
            }

            return(Ok(executionId));
        }
Ejemplo n.º 15
0
 public Task RecipeExecutingAsync(string executionId, RecipeDescriptor descriptor) => Task.CompletedTask;
Ejemplo n.º 16
0
        public async Task <ActionResult> IndexPOST(SetupViewModel model)
        {
            if (!string.IsNullOrWhiteSpace(_shellSettings["Secret"]))
            {
                if (string.IsNullOrEmpty(model.Secret) || !await IsTokenValid(model.Secret))
                {
                    _logger.LogWarning("An attempt to access '{TenantName}' without providing a valid secret was made", _shellSettings.Name);
                    return(StatusCode(404));
                }
            }

            model.DatabaseProviders = _databaseProviders;
            model.Recipes           = await _setupService.GetSetupRecipesAsync();

            var selectedProvider = model.DatabaseProviders.FirstOrDefault(x => x.Value == model.DatabaseProvider);

            if (!model.DatabaseConfigurationPreset)
            {
                if (selectedProvider != null && selectedProvider.HasConnectionString && String.IsNullOrWhiteSpace(model.ConnectionString))
                {
                    ModelState.AddModelError(nameof(model.ConnectionString), S["The connection string is mandatory for this provider."]);
                }
            }

            if (String.IsNullOrEmpty(model.Password))
            {
                ModelState.AddModelError(nameof(model.Password), S["The password is required."]);
            }

            if (model.Password != model.PasswordConfirmation)
            {
                ModelState.AddModelError(nameof(model.PasswordConfirmation), S["The password confirmation doesn't match the password."]);
            }

            RecipeDescriptor selectedRecipe = null;

            if (!string.IsNullOrEmpty(_shellSettings["RecipeName"]))
            {
                selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == _shellSettings["RecipeName"]);
                if (selectedRecipe == null)
                {
                    ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]);
                }
            }
            else if (String.IsNullOrEmpty(model.RecipeName) || (selectedRecipe = model.Recipes.FirstOrDefault(x => x.Name == model.RecipeName)) == null)
            {
                ModelState.AddModelError(nameof(model.RecipeName), S["Invalid recipe."]);
            }

            // Only add additional errors if attribute validation has passed.
            if (!String.IsNullOrEmpty(model.Email) && !_emailAddressValidator.Validate(model.Email))
            {
                ModelState.AddModelError(nameof(model.Email), S["The email is invalid."]);
            }

            if (!String.IsNullOrEmpty(model.UserName) && model.UserName.Any(c => !_identityOptions.User.AllowedUserNameCharacters.Contains(c)))
            {
                ModelState.AddModelError(nameof(model.UserName), S["User name '{0}' is invalid, can only contain letters or digits.", model.UserName]);
            }

            if (!ModelState.IsValid)
            {
                CopyShellSettingsValues(model);
                return(View(model));
            }

            var setupContext = new SetupContext
            {
                ShellSettings   = _shellSettings,
                EnabledFeatures = null, // default list,
                Errors          = new Dictionary <string, string>(),
                Recipe          = selectedRecipe,
                Properties      = new Dictionary <string, object>
                {
                    { SetupConstants.SiteName, model.SiteName },
                    { SetupConstants.AdminUsername, model.UserName },
                    { SetupConstants.AdminEmail, model.Email },
                    { SetupConstants.AdminPassword, model.Password },
                    { SetupConstants.SiteTimeZone, model.SiteTimeZone },
                }
            };

            if (!string.IsNullOrEmpty(_shellSettings["ConnectionString"]))
            {
                setupContext.Properties[SetupConstants.DatabaseProvider]         = _shellSettings["DatabaseProvider"];
                setupContext.Properties[SetupConstants.DatabaseConnectionString] = _shellSettings["ConnectionString"];
                setupContext.Properties[SetupConstants.DatabaseTablePrefix]      = _shellSettings["TablePrefix"];
            }
            else
            {
                setupContext.Properties[SetupConstants.DatabaseProvider]         = model.DatabaseProvider;
                setupContext.Properties[SetupConstants.DatabaseConnectionString] = model.ConnectionString;
                setupContext.Properties[SetupConstants.DatabaseTablePrefix]      = model.TablePrefix;
            }

            var executionId = await _setupService.SetupAsync(setupContext);

            // Check if a component in the Setup failed
            if (setupContext.Errors.Any())
            {
                foreach (var error in setupContext.Errors)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }

                return(View(model));
            }

            return(Redirect("~/"));
        }
Ejemplo n.º 17
0
        public async Task <string> ExecuteAsync(string executionId, RecipeDescriptor recipeDescriptor)
        {
            await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutingAsync(executionId, recipeDescriptor));

            try
            {
                RecipeResult result = new RecipeResult {
                    ExecutionId = executionId
                };
                List <RecipeStepResult> stepResults = new List <RecipeStepResult>();

                using (StreamReader file = File.OpenText(recipeDescriptor.RecipeFileInfo.PhysicalPath))
                {
                    using (var reader = new JsonTextReader(file))
                    {
                        VariablesMethodProvider variablesMethodProvider = null;

                        // Go to Steps, then iterate.
                        while (reader.Read())
                        {
                            if (reader.Path == "variables")
                            {
                                reader.Read();

                                var variables = JObject.Load(reader);
                                variablesMethodProvider = new VariablesMethodProvider(variables);
                            }

                            if (reader.Path == "steps" && reader.TokenType == JsonToken.StartArray)
                            {
                                int stepId = 0;
                                while (reader.Read() && reader.Depth > 1)
                                {
                                    if (reader.Depth == 2)
                                    {
                                        var child = JToken.Load(reader);

                                        var recipeStep = new RecipeStepDescriptor
                                        {
                                            Id   = (stepId++).ToString(),
                                            Name = child.Value <string>("name"),
                                            Step = child
                                        };

                                        var shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings);
                                        using (var scope = shellContext.CreateServiceScope())
                                        {
                                            if (!shellContext.IsActivated)
                                            {
                                                var eventBus = scope.ServiceProvider.GetService <IEventBus>();
                                                await eventBus.NotifyAsync <IOrchardShellEvents>(x => x.ActivatingAsync());

                                                await eventBus.NotifyAsync <IOrchardShellEvents>(x => x.ActivatedAsync());

                                                shellContext.IsActivated = true;
                                            }

                                            var recipeStepExecutor = scope.ServiceProvider.GetRequiredService <IRecipeStepExecutor>();
                                            var scriptingManager   = scope.ServiceProvider.GetRequiredService <IScriptingManager>();

                                            if (variablesMethodProvider != null)
                                            {
                                                variablesMethodProvider.ScriptingManager = scriptingManager;
                                                scriptingManager.GlobalMethodProviders.Add(variablesMethodProvider);
                                            }

                                            if (_applicationLifetime.ApplicationStopping.IsCancellationRequested)
                                            {
                                                throw new OrchardException(T["Recipe cancelled, application is restarting"]);
                                            }

                                            EvaluateJsonTree(scriptingManager, recipeStep.Step);

                                            await recipeStepExecutor.ExecuteAsync(executionId, recipeStep);
                                        }

                                        // The recipe execution might have invalidated the shell by enabling new features,
                                        // so the deferred tasks need to run on an updated shell context if necessary.
                                        shellContext = _orchardHost.GetOrCreateShellContext(_shellSettings);
                                        using (var scope = shellContext.CreateServiceScope())
                                        {
                                            var recipeStepExecutor = scope.ServiceProvider.GetRequiredService <IRecipeStepExecutor>();

                                            var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>();

                                            // The recipe might have added some deferred tasks to process
                                            if (deferredTaskEngine != null && deferredTaskEngine.HasPendingTasks)
                                            {
                                                var taskContext = new DeferredTaskContext(scope.ServiceProvider);
                                                await deferredTaskEngine.ExecuteTasksAsync(taskContext);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.RecipeExecutedAsync(executionId, recipeDescriptor));

                return(executionId);
            }
            catch (Exception)
            {
                await _eventBus.NotifyAsync <IRecipeEventHandler>(x => x.ExecutionFailedAsync(executionId, recipeDescriptor));

                throw;
            }
        }
Ejemplo n.º 18
0
        public async Task <ActionResult> Setup(SetupApiViewModel model)
        {
            if (!IsDefaultShell())
            {
                return(this.ChallengeOrForbid("Api"));
            }

            if (!await _authorizationService.AuthorizeAsync(User, Permissions.ManageTenants))
            {
                return(this.ChallengeOrForbid("Api"));
            }

            if (!String.IsNullOrEmpty(model.UserName) && model.UserName.Any(c => !_identityOptions.User.AllowedUserNameCharacters.Contains(c)))
            {
                ModelState.AddModelError(nameof(model.UserName), S["User name '{0}' is invalid, can only contain letters or digits.", model.UserName]);
            }

            // Only add additional error if attribute validation has passed.
            if (!String.IsNullOrEmpty(model.Email) && !_emailAddressValidator.Validate(model.Email))
            {
                ModelState.AddModelError(nameof(model.Email), S["The email is invalid."]);
            }

            if (!ModelState.IsValid)
            {
                return(BadRequest());
            }

            if (!_shellHost.TryGetSettings(model.Name, out var shellSettings))
            {
                ModelState.AddModelError(nameof(SetupApiViewModel.Name), S["Tenant not found: '{0}'", model.Name]);
            }

            if (shellSettings.State == TenantState.Running)
            {
                return(StatusCode(201));
            }

            if (shellSettings.State != TenantState.Uninitialized)
            {
                return(BadRequest(S["The tenant can't be setup."]));
            }

            var databaseProvider = shellSettings["DatabaseProvider"];

            if (String.IsNullOrEmpty(databaseProvider))
            {
                databaseProvider = model.DatabaseProvider;
            }

            var selectedProvider = _databaseProviders.FirstOrDefault(x => String.Equals(x.Value, databaseProvider, StringComparison.OrdinalIgnoreCase));

            if (selectedProvider == null)
            {
                return(BadRequest(S["The database provider is not defined."]));
            }

            var tablePrefix = shellSettings["TablePrefix"];

            if (String.IsNullOrEmpty(tablePrefix))
            {
                tablePrefix = model.TablePrefix;
            }

            var connectionString = shellSettings["connectionString"];

            if (String.IsNullOrEmpty(connectionString))
            {
                connectionString = model.ConnectionString;
            }

            if (selectedProvider.HasConnectionString && String.IsNullOrEmpty(connectionString))
            {
                return(BadRequest(S["The connection string is required for this database provider."]));
            }

            var recipeName = shellSettings["RecipeName"];

            if (String.IsNullOrEmpty(recipeName))
            {
                recipeName = model.RecipeName;
            }

            RecipeDescriptor recipeDescriptor = null;

            if (String.IsNullOrEmpty(recipeName))
            {
                if (model.Recipe == null)
                {
                    return(BadRequest(S["Either 'Recipe' or 'RecipeName' is required."]));
                }

                var tempFilename = Path.GetTempFileName();

                using (var fs = System.IO.File.Create(tempFilename))
                {
                    await model.Recipe.CopyToAsync(fs);
                }

                var fileProvider = new PhysicalFileProvider(Path.GetDirectoryName(tempFilename));

                recipeDescriptor = new RecipeDescriptor
                {
                    FileProvider   = fileProvider,
                    BasePath       = "",
                    RecipeFileInfo = fileProvider.GetFileInfo(Path.GetFileName(tempFilename))
                };
            }
            else
            {
                var setupRecipes = await _setupService.GetSetupRecipesAsync();

                recipeDescriptor = setupRecipes.FirstOrDefault(x => String.Equals(x.Name, recipeName, StringComparison.OrdinalIgnoreCase));

                if (recipeDescriptor == null)
                {
                    return(BadRequest(S["Recipe '{0}' not found.", recipeName]));
                }
            }

            var setupContext = new SetupContext
            {
                ShellSettings   = shellSettings,
                EnabledFeatures = null, // default list,
                Errors          = new Dictionary <string, string>(),
                Recipe          = recipeDescriptor,
                Properties      = new Dictionary <string, object>
                {
                    { SetupConstants.SiteName, model.SiteName },
                    { SetupConstants.AdminUsername, model.UserName },
                    { SetupConstants.AdminEmail, model.Email },
                    { SetupConstants.AdminPassword, model.Password },
                    { SetupConstants.SiteTimeZone, model.SiteTimeZone },
                    { SetupConstants.DatabaseProvider, selectedProvider.Value },
                    { SetupConstants.DatabaseConnectionString, connectionString },
                    { SetupConstants.DatabaseTablePrefix, tablePrefix },
                }
            };

            var executionId = await _setupService.SetupAsync(setupContext);

            // Check if a component in the Setup failed
            if (setupContext.Errors.Any())
            {
                foreach (var error in setupContext.Errors)
                {
                    ModelState.AddModelError(error.Key, error.Value);
                }

                return(StatusCode(500, ModelState));
            }

            return(Ok(executionId));
        }
Ejemplo n.º 19
0
        public async Task <string> ExecuteAsync(string executionId, RecipeDescriptor recipeDescriptor, object environment, CancellationToken cancellationToken)
        {
            await _recipeEventHandlers.InvokeAsync((handler, executionId, recipeDescriptor) => handler.RecipeExecutingAsync(executionId, recipeDescriptor), executionId, recipeDescriptor, _logger);

            try
            {
                _environmentMethodProvider   = new ParametersMethodProvider(environment);
                _configurationMethodProvider = new ConfigurationMethodProvider(_shellSettings.ShellConfiguration);

                var result = new RecipeResult {
                    ExecutionId = executionId
                };

                using (var stream = recipeDescriptor.RecipeFileInfo.CreateReadStream())
                {
                    using (var file = new StreamReader(stream))
                    {
                        using (var reader = new JsonTextReader(file))
                        {
                            // Go to Steps, then iterate.
                            while (await reader.ReadAsync())
                            {
                                if (reader.Path == "variables")
                                {
                                    await reader.ReadAsync();

                                    var variables = await JObject.LoadAsync(reader);

                                    _variablesMethodProvider = new VariablesMethodProvider(variables);
                                }

                                if (reader.Path == "steps" && reader.TokenType == JsonToken.StartArray)
                                {
                                    while (await reader.ReadAsync() && reader.Depth > 1)
                                    {
                                        if (reader.Depth == 2)
                                        {
                                            var child = await JObject.LoadAsync(reader);

                                            var recipeStep = new RecipeExecutionContext
                                            {
                                                Name             = child.Value <string>("name"),
                                                Step             = child,
                                                ExecutionId      = executionId,
                                                Environment      = environment,
                                                RecipeDescriptor = recipeDescriptor
                                            };

                                            if (cancellationToken.IsCancellationRequested)
                                            {
                                                _logger.LogError("Recipe interrupted by cancellation token.");
                                                return(null);
                                            }

                                            var stepResult = new RecipeStepResult {
                                                StepName = recipeStep.Name
                                            };
                                            result.Steps.Add(stepResult);

                                            ExceptionDispatchInfo capturedException = null;
                                            try
                                            {
                                                await ExecuteStepAsync(recipeStep);

                                                stepResult.IsSuccessful = true;
                                            }
                                            catch (Exception e)
                                            {
                                                stepResult.IsSuccessful = false;
                                                stepResult.ErrorMessage = e.ToString();

                                                // Because we can't do some async processing the in catch or finally
                                                // blocks, we store the exception to throw it later.

                                                capturedException = ExceptionDispatchInfo.Capture(e);
                                            }

                                            stepResult.IsCompleted = true;

                                            if (stepResult.IsSuccessful == false)
                                            {
                                                capturedException.Throw();
                                            }

                                            if (recipeStep.InnerRecipes != null)
                                            {
                                                foreach (var descriptor in recipeStep.InnerRecipes)
                                                {
                                                    var innerExecutionId = Guid.NewGuid().ToString();
                                                    await ExecuteAsync(innerExecutionId, descriptor, environment, cancellationToken);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                await _recipeEventHandlers.InvokeAsync((handler, executionId, recipeDescriptor) => handler.RecipeExecutedAsync(executionId, recipeDescriptor), executionId, recipeDescriptor, _logger);

                return(executionId);
            }
            catch (Exception)
            {
                await _recipeEventHandlers.InvokeAsync((handler, executionId, recipeDescriptor) => handler.ExecutionFailedAsync(executionId, recipeDescriptor), executionId, recipeDescriptor, _logger);

                throw;
            }
        }