private static void LoadRadiusReplyAttributes(ClientConfiguration configuration, IRadiusDictionary dictionary, RadiusReplyAttributesSection radiusReplyAttributesSection)
        {
            var replyAttributes = new Dictionary <string, List <RadiusReplyAttributeValue> >();

            if (radiusReplyAttributesSection != null)
            {
                foreach (var member in radiusReplyAttributesSection.Members)
                {
                    var attribute       = member as RadiusReplyAttributeElement;
                    var radiusAttribute = dictionary.GetAttribute(attribute.Name);
                    if (radiusAttribute == null)
                    {
                        throw new ConfigurationErrorsException($"Unknown attribute '{attribute.Name}' in RadiusReply configuration element, please see dictionary");
                    }

                    if (!replyAttributes.ContainsKey(attribute.Name))
                    {
                        replyAttributes.Add(attribute.Name, new List <RadiusReplyAttributeValue>());
                    }

                    if (!string.IsNullOrEmpty(attribute.From))
                    {
                        replyAttributes[attribute.Name].Add(new RadiusReplyAttributeValue(attribute.From));
                    }
                    else
                    {
                        try
                        {
                            var value = ParseRadiusReplyAttributeValue(radiusAttribute, attribute.Value);
                            replyAttributes[attribute.Name].Add(new RadiusReplyAttributeValue(value, attribute.When));
                        }
                        catch (Exception ex)
                        {
                            throw new ConfigurationErrorsException($"Error while parsing attribute '{radiusAttribute.Name}' with {radiusAttribute.Type} value '{attribute.Value}' in RadiusReply configuration element: {ex.Message}");
                        }
                    }
                }
            }

            configuration.RadiusReplyAttributes = replyAttributes;
        }
        public static ClientConfiguration Load(string name, IRadiusDictionary dictionary, AppSettingsSection appSettings, RadiusReplyAttributesSection radiusReplyAttributesSection, bool requiresClientIdentification)
        {
            var radiusClientNasIdentifierSetting            = appSettings.Settings["radius-client-nas-identifier"]?.Value;
            var radiusClientIpSetting                       = appSettings.Settings["radius-client-ip"]?.Value;
            var radiusSharedSecretSetting                   = appSettings.Settings["radius-shared-secret"]?.Value;
            var firstFactorAuthenticationSourceSettings     = appSettings.Settings["first-factor-authentication-source"]?.Value;
            var bypassSecondFactorWhenApiUnreachableSetting = appSettings.Settings["bypass-second-factor-when-api-unreachable"]?.Value;
            var multiFactorApiKeySetting                    = appSettings.Settings["multifactor-nas-identifier"]?.Value;
            var multiFactorApiSecretSetting                 = appSettings.Settings["multifactor-shared-secret"]?.Value;

            if (string.IsNullOrEmpty(firstFactorAuthenticationSourceSettings))
            {
                throw new Exception("Configuration error: 'first-factor-authentication-source' element not found");
            }

            if (string.IsNullOrEmpty(radiusSharedSecretSetting))
            {
                throw new Exception("Configuration error: 'radius-shared-secret' element not found");
            }

            if (string.IsNullOrEmpty(multiFactorApiKeySetting))
            {
                throw new Exception("Configuration error: 'multifactor-nas-identifier' element not found");
            }
            if (string.IsNullOrEmpty(multiFactorApiSecretSetting))
            {
                throw new Exception("Configuration error: 'multifactor-shared-secret' element not found");
            }

            if (!Enum.TryParse <AuthenticationSource>(firstFactorAuthenticationSourceSettings, out var firstFactorAuthenticationSource))
            {
                throw new Exception("Configuration error: Can't parse 'first-factor-authentication-source' value. Must be one of: ActiveDirectory, Radius, None");
            }

            var configuration = new ClientConfiguration
            {
                Name = name,
                RadiusSharedSecret = radiusSharedSecretSetting,
                FirstFactorAuthenticationSource = firstFactorAuthenticationSource,
                MultifactorApiKey    = multiFactorApiKeySetting,
                MultiFactorApiSecret = multiFactorApiSecretSetting,
            };

            if (requiresClientIdentification)
            {
                if (string.IsNullOrEmpty(radiusClientNasIdentifierSetting) && string.IsNullOrEmpty(radiusClientIpSetting))
                {
                    throw new Exception("Configuration error: Either 'radius-client-nas-identifier' or 'radius-client-ip' must be configured");
                }
                if (!string.IsNullOrEmpty(radiusClientNasIdentifierSetting))
                {
                    configuration.NasIdentifier = radiusClientNasIdentifierSetting;
                }
                else
                {
                    if (!IPAddress.TryParse(radiusClientIpSetting, out var clientIpAddress))
                    {
                        throw new Exception("Configuration error: Can't parse 'radius-client-ip' value. Must be valid IPv4 address");
                    }
                    configuration.Ip = clientIpAddress;
                }
            }

            if (bypassSecondFactorWhenApiUnreachableSetting != null)
            {
                if (bool.TryParse(bypassSecondFactorWhenApiUnreachableSetting, out var bypassSecondFactorWhenApiUnreachable))
                {
                    configuration.BypassSecondFactorWhenApiUnreachable = bypassSecondFactorWhenApiUnreachable;
                }
            }

            switch (configuration.FirstFactorAuthenticationSource)
            {
            case AuthenticationSource.ActiveDirectory:
            case AuthenticationSource.Ldap:
                LoadActiveDirectoryAuthenticationSourceSettings(configuration, appSettings);
                break;

            case AuthenticationSource.Radius:
                LoadRadiusAuthenticationSourceSettings(configuration, appSettings);
                break;
            }

            LoadRadiusReplyAttributes(configuration, dictionary, radiusReplyAttributesSection);

            return(configuration);
        }