/// <summary> /// Creates a default <see cref="Models.Instance"/> from <paramref name="initialSettings"/>. /// </summary> /// <param name="initialSettings">The <see cref="InstanceCreateRequest"/>.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param> /// <returns>A <see cref="Task{TResult}"/> resulting in the new <see cref="Models.Instance"/> or <see langword="null"/> if ports could not be allocated.</returns> async Task <Models.Instance> CreateDefaultInstance(InstanceCreateRequest initialSettings, CancellationToken cancellationToken) { var ddPort = await portAllocator.GetAvailablePort(1, false, cancellationToken).ConfigureAwait(false); if (!ddPort.HasValue) { return(null); } // try to use the old default if possible const ushort DefaultDreamDaemonPort = 1337; if (ddPort.Value < DefaultDreamDaemonPort) { ddPort = await portAllocator.GetAvailablePort(DefaultDreamDaemonPort, false, cancellationToken).ConfigureAwait(false) ?? ddPort; } const ushort DefaultApiValidationPort = 1339; var dmPort = await portAllocator .GetAvailablePort( Math.Min((ushort)(ddPort.Value + 1), DefaultApiValidationPort), false, cancellationToken) .ConfigureAwait(false); if (!dmPort.HasValue) { return(null); } // try to use the old default if possible if (dmPort < DefaultApiValidationPort) { dmPort = await portAllocator.GetAvailablePort(DefaultApiValidationPort, false, cancellationToken).ConfigureAwait(false) ?? dmPort; } return(new Models.Instance { ConfigurationType = initialSettings.ConfigurationType ?? ConfigurationType.Disallowed, DreamDaemonSettings = new DreamDaemonSettings { AllowWebClient = false, AutoStart = false, Port = ddPort, SecurityLevel = DreamDaemonSecurity.Safe, StartupTimeout = 60, HeartbeatSeconds = 60, TopicRequestTimeout = generalConfiguration.ByondTopicTimeout, AdditionalParameters = String.Empty, }, DreamMakerSettings = new DreamMakerSettings { ApiValidationPort = dmPort, ApiValidationSecurityLevel = DreamDaemonSecurity.Safe, RequireDMApiValidation = true }, Name = initialSettings.Name, Online = false, Path = initialSettings.Path, AutoUpdateInterval = initialSettings.AutoUpdateInterval ?? 0, ChatBotLimit = initialSettings.ChatBotLimit ?? Models.Instance.DefaultChatBotLimit, RepositorySettings = new RepositorySettings { CommitterEmail = Components.Repository.Repository.DefaultCommitterEmail, CommitterName = Components.Repository.Repository.DefaultCommitterName, PushTestMergeCommits = false, ShowTestMergeCommitters = false, AutoUpdatesKeepTestMerges = false, AutoUpdatesSynchronize = false, PostTestMergeComment = false, CreateGitHubDeployments = false }, InstancePermissionSets = new List <InstancePermissionSet> // give this user full privileges on the instance { InstanceAdminPermissionSet(null) }, SwarmIdentifer = swarmConfiguration.Identifier, }); }
public async Task <IActionResult> Update([FromBody] DreamMakerRequest model, CancellationToken cancellationToken) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (model.ApiValidationPort == 0) { throw new InvalidOperationException("ApiValidationPort cannot be 0!"); } var hostModel = await DatabaseContext .DreamMakerSettings .AsQueryable() .Where(x => x.InstanceId == Instance.Id) .FirstOrDefaultAsync(cancellationToken) .ConfigureAwait(false); if (hostModel == null) { return(Gone()); } if (model.ProjectName != null) { if (!AuthenticationContext.InstancePermissionSet.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetDme)) { return(Forbid()); } if (model.ProjectName.Length == 0) { hostModel.ProjectName = null; } else { hostModel.ProjectName = model.ProjectName; } } if (model.ApiValidationPort.HasValue) { if (!AuthenticationContext.InstancePermissionSet.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetApiValidationPort)) { return(Forbid()); } if (model.ApiValidationPort.Value != hostModel.ApiValidationPort.Value) { var verifiedPort = await portAllocator .GetAvailablePort( model.ApiValidationPort.Value, true, cancellationToken) .ConfigureAwait(false); if (verifiedPort != model.ApiValidationPort) { return(Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable))); } hostModel.ApiValidationPort = model.ApiValidationPort; } } if (model.ApiValidationSecurityLevel.HasValue) { if (!AuthenticationContext.InstancePermissionSet.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetSecurityLevel)) { return(Forbid()); } hostModel.ApiValidationSecurityLevel = model.ApiValidationSecurityLevel; } if (model.RequireDMApiValidation.HasValue) { if (!AuthenticationContext.InstancePermissionSet.DreamMakerRights.Value.HasFlag(DreamMakerRights.SetApiValidationRequirement)) { return(Forbid()); } hostModel.RequireDMApiValidation = model.RequireDMApiValidation; } await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); if ((AuthenticationContext.GetRight(RightsType.DreamMaker) & (ulong)DreamMakerRights.Read) == 0) { return(NoContent()); } return(await Read(cancellationToken).ConfigureAwait(false)); }
#pragma warning disable CA1502 // TODO: Decomplexify #pragma warning disable CA1506 public async Task <IActionResult> Update([FromBody] DreamDaemonResponse model, CancellationToken cancellationToken) { if (model == null) { throw new ArgumentNullException(nameof(model)); } if (model.SoftShutdown == true && model.SoftRestart == true) { return(BadRequest(new ErrorMessageResponse(ErrorCode.DreamDaemonDoubleSoft))); } // alias for changing DD settings var current = await DatabaseContext .Instances .AsQueryable() .Where(x => x.Id == Instance.Id) .Select(x => x.DreamDaemonSettings) .FirstOrDefaultAsync(cancellationToken) .ConfigureAwait(false); if (current == default) { return(Gone()); } if (model.Port.HasValue && model.Port.Value != current.Port.Value) { var verifiedPort = await portAllocator .GetAvailablePort( model.Port.Value, true, cancellationToken) .ConfigureAwait(false); if (verifiedPort != model.Port) { return(Conflict(new ErrorMessageResponse(ErrorCode.PortNotAvailable))); } } var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon); bool CheckModified <T>(Expression <Func <Api.Models.Internal.DreamDaemonSettings, T> > expression, DreamDaemonRights requiredRight) { var memberSelectorExpression = (MemberExpression)expression.Body; var property = (PropertyInfo)memberSelectorExpression.Member; var newVal = property.GetValue(model); if (newVal == null) { return(false); } if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal) { return(true); } property.SetValue(current, newVal); return(false); } return(await WithComponentInstance( async instance => { var watchdog = instance.Watchdog; var rebootState = watchdog.RebootState; var oldSoftRestart = rebootState == RebootState.Restart; var oldSoftShutdown = rebootState == RebootState.Shutdown; if (CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient) || CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart) || CheckModified(x => x.Port, DreamDaemonRights.SetPort) || CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity) || (model.SoftRestart.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftRestart)) || (model.SoftShutdown.HasValue && !AuthenticationContext.InstancePermissionSet.DreamDaemonRights.Value.HasFlag(DreamDaemonRights.SoftShutdown)) || CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout) || CheckModified(x => x.HeartbeatSeconds, DreamDaemonRights.SetHeartbeatInterval) || CheckModified(x => x.TopicRequestTimeout, DreamDaemonRights.SetTopicTimeout) || CheckModified(x => x.AdditionalParameters, DreamDaemonRights.SetAdditionalParameters)) { return Forbid(); } await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); // run this second because current may be modified by it await watchdog.ChangeSettings(current, cancellationToken).ConfigureAwait(false); if (!oldSoftRestart && model.SoftRestart == true && watchdog.Status == WatchdogStatus.Online) { await watchdog.Restart(true, cancellationToken).ConfigureAwait(false); } else if (!oldSoftShutdown && model.SoftShutdown == true) { await watchdog.Terminate(true, cancellationToken).ConfigureAwait(false); } else if ((oldSoftRestart && model.SoftRestart == false) || (oldSoftShutdown && model.SoftShutdown == false)) { await watchdog.ResetRebootState(cancellationToken).ConfigureAwait(false); } return await ReadImpl(current, cancellationToken).ConfigureAwait(false); }) .ConfigureAwait(false)); }