/// <summary> /// Attempts to load all secret values for the specified object. /// </summary> /// <param name="obj"> /// The object where secret values will be loaded. /// </param> static public void LoadSecrets(object obj) { // Validate if (obj == null) { throw new ArgumentNullException(nameof(obj)); } // Get all fields FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); // Look for secret fields foreach (var field in fields) { // Try to get attribute SecretValueAttribute sva = field.GetCustomAttribute <SecretValueAttribute>(); // If not a secret, skip if (sva == null) { continue; } // Try to load the value TryLoadValue(sva, field, obj); } }
/// <summary> /// Attempts to load a secret value into the specified field. /// </summary> /// <param name="sva"> /// A <see cref="SecretValueAttribute"/> that indicates the source of the secret value. /// </param> /// <param name="field"> /// The field where the value will be loaded. /// </param> /// <param name="obj"> /// The object instance where the value will be set. /// </param> /// <param name="overwrite"> /// <c>true</c> to overwrite non-default values; otherwise <c>false</c>. The default is <c>false</c>. /// </param> /// <remarks> /// By default <see cref="SecretHelper"/> will only update fields that are set to default values /// (e.g. 0 for int and null or "" for string). This allows values set in the Unity inspector to /// override values stored in the environment. If values in the environment should always take /// precedence over values stored in the field set <paramref name="overwrite"/> to <c>true</c>. /// </remarks> static private void TryLoadValue(SecretValueAttribute sva, FieldInfo field, object obj, bool overwrite = false) { // Validate if (sva == null) { throw new ArgumentNullException(nameof(sva)); } if (field == null) { throw new ArgumentNullException(nameof(field)); } if (obj == null) { throw new ArgumentNullException(nameof(obj)); } // Now get the current value of the field object curValue = field.GetValue(obj); // If we're not overwriting values, we need to check to check and make sure a non-default value is not already set if (!overwrite) { // What is the default value for the field? object defValue = GetDefaultValue(field.FieldType); // Is it the current value the same as the default value? bool isDefaultValue = ((curValue == defValue) || ((field.FieldType == typeof(string)) && (string.IsNullOrEmpty((string)curValue)))); // If the current value is not the default value, the secret has already been supplied // and we don't need to do any more work. if (!isDefaultValue) { return; } } // Either in overwrite mode or a default value. Let's try to read the environment variable. string svalue = Environment.GetEnvironmentVariable(sva.Name); // Check for no environment variable or no value set. if (string.IsNullOrEmpty(svalue)) { Debug.LogWarning($"{obj.GetType().Name}.{field.Name} has the default value '{curValue}' but the environment variable {sva.Name} is missing or not set."); return; } // If string, just assign. Otherwise attempt to convert. if (field.FieldType == typeof(string)) { field.SetValue(obj, svalue); } else { try { object cvalue = Convert.ChangeType(svalue, field.FieldType); field.SetValue(obj, cvalue); } catch (Exception ex) { Debug.LogWarning($"The value '{svalue}' of environment variable {sva.Name} could not be converted to {field.FieldType.Name}. {ex.Message}"); } } }