예제 #1
0
        /// <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,
            });
        }
예제 #2
0
        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));
        }