Advanced-Config

 view release on metacpan or  search on metacpan

Changes  view on Meta::CPAN

    of DBUG required.
  - Modified all t/*.t test progs to use Fred::Fish::DBUG 2.01 qw / on /;

1.08 2020-03-16 08:30:00
  - Config.pm - Fixed most functions not exposed by POD to use a leading
    underscore in their names to be more consistent.  The remaining exceptions
    used to be exposed in POD and are being depreciated.  As always anything
    beginning with an underscore is subject to change without notice.
  - Date.pm - Major rewrite of parse_date() in advance of using Date::Manip as
    an alternate source to getting foreign language support.
  - Date.pm - Adding Date::Manip logic.  Greatly expands the number of languages
    and date formats allowed.
  - t/76-check_all_languages2.t - Test case to support using Date::Manip.

1.07 2020-02-20 08:30:00
  - Updated copyrights to 2020 on all files.
  - Made some corrections to the README file.
  - Reader.pm - Added an optional trim flag to expand_variables().
  - Reader.pm - Fixed balanced quote bug in parse_line().
  - Fixed t/60-recursion-test.t & 60-recursion-test.cfg to handle trim
    properly.
  - 40-validate-modifiers.cfg - Modified to highlight the parse_line()
    balanced quote issue was fixed.
  - Reader.pm - Fixed disable quotes bug by checking 1st in parse_line()
    quote detection section.
  - Config.pm - Changed section() to get_section(), depreciating section()
    with a stub function that prints warning if used.
  - Config.pm - Added create_section() and no longer exposed new_section()

Changes  view on Meta::CPAN

1.06 2019-11-01 08:30:00
  - Removed the DBUG_REGISTER call from all modules since it's definition
    changed in DBUG (v1.08) and was depreciated.
  - Added the 3 back quote options for allowing your config files to run
    commands to set a tag's value per a user request.  (Disabled by default)
  - Then removed it for being too much of a security concern.  Left the options
    in as comments so I can remember why I backed it out until I can figure out
    better protection from malicious actors.
  - Improved disable tests in t/20-sensitive.t & it's config file.
  - Some minor POD corrections to all the modules.
  - Fixed expand_variables() in Reader.pm to continue expanding non-encrypted
    variables after an encrypted one was hit when option "disable_decryption"
    is used.
  - Fixed POD in Options.pm to reflect how "disable_decryption" now works
    with variables enabled.
  - Added 27-disable_decrypt_test.t to test out this feature.
  - Fixed tests t/28-sensitive_tests.t & t/35-improper_tests.t which broke
    when we fixed expand_variables().
  Never uploaded to CPAN.

1.05 2019-05-30 08:30:00
  - Added print_special_vars() as a quick & dirty way to get a list of the
    special variables supported by this module.
  - Added "section" as a new special variable!
  - Added "section" test to t/12-validate_sections.t to validate the new
    variable works correctly.
  - Options.pm - Added "use_gmt" as a new "Special Date Variable Formatting
    Option" to allow the use of gmtime instead of localtime when calculating

Config.pm  view on Meta::CPAN


   DBUG_RETURN ( $encrypted );
}


#######################################

=item $bool = $cfg->chk_if_still_uses_variables ( $tag[, $override_inherit] );

This function looks up the requested tag in the current section of the config
file and returns if the tag's value contained variables that failed to expand
when the config file was parsed.  (B<1> - has variable, B<0> - none.)

If the tag doesn't exist, or you called C<set_value> to create it, this function
will always return B<0> for that tag!

There are only two cases where it can ever return true (B<1>).  The first case
is when you used the B<disable_variables> option.  The second case is if you
used the B<disable_decryption> option and you had a variable that referenced
a tag that is still encrypted.  But use of those two options should be rare.

lib/Advanced/Config/Examples.pm  view on Meta::CPAN


For more on this see the following link on Parameter Expansion:
L<https://web.archive.org/web/20200309072646/https://wiki.bash-hackers.org/syntax/pe>
This module supports most of the parameter expansions listed in the link except
for those dealing with arrays.  Other modifier rules may be added upon request.

Things get a bit more complex evaluating variables if you've defined sections
in your config file.

See the POD for B<lookup_one_variable>() in L<Advanced::Config> for step by step
instructions on expanding a variable's name.

For a list of special variables try calling:
S<Advanced::Config-E<gt>print_special_vars();>

=head1 SOURCING IN OTHER CONFIG FILES

It's possible to source multiple config files together as if they were one big
config file.  You can either use absolute paths to each config file, or more
likely relative paths.

lib/Advanced/Config/Options.pm  view on Meta::CPAN

B<disable_variable_modifiers> - Defaults to B<0>.  Set to B<1> if you want to
disable this feature.  See L<http://wiki.bash-hackers.org/syntax/pe> for more
details.  This feature allows you to put logic into your config files via
your variable definitions.  Automatically disabled when variables are
disabled.  Useful when you put a lot of special chars into your variable
names.

B<disable_decryption> - Defaults to B<0>.  Set to B<1> if you want to disable
decrypting values that have been marked as encrypted.  If a variable references
an encrypted value while disable_decription is active, that variable isn't
expanded.

=cut 

# B<enable_backquotes> - Defaults to B<0>.  Set to B<1> if you want to enable
# this feature.  It's disabled by default since it can be considered a security
# hole if an unauthorized user can modify your config file or your code.

=pod

B<trap_recursion> - Defaults to B<0>.  Set to B<1> if you want to treat

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

use Advanced::Config;

use Fred::Fish::DBUG 2.09 qw / on_if_set  ADVANCED_CONFIG_FISH /;

use File::Basename;

$VERSION = "1.14";
@ISA = qw( Exporter );

@EXPORT = qw( read_config  source_file  make_new_section  parse_line
              expand_variables  apply_modifier  parse_for_variables
              format_section_line  format_tag_value_line format_encrypt_cmt
              encrypt_config_file_details  decrypt_config_file_details );

@EXPORT_OK = qw( );

my $skip_warns_due_to_make_test;
my %global_sections;
my $gUserName;

# ==============================================================

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

            $hide_section{$section} = 0;   # Assume not sensitive ...

            if ( $cmt =~ m/(^|${lbl_sep})${hide_str}(${lbl_sep}|$)/ ||
                 should_we_hide_sensitive_data ( $section ) ) {
               $hide_section{$section} = 1;
            }
            next;
         }

         # Don't know what the config file was thinking of ...
         # Don't bother expanding any variables encountered.
         DBUG_PRINT ("error", "<Previous line ignored.  Unknown format!>");
         next;
      }

      # ------------------------------------------------------------------
      # If you get here, you know it's a tag/value pair to parse ...
      # Don't forget that any comment can include processing instructions!
      # ------------------------------------------------------------------

      # Go to the requested section ...

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

      if ( $cmt =~ m/(^|${lbl_sep})${decrypt_str}(${lbl_sep}|$)/ ) {
         $value = _reverse_escape_sequences ( $value, $opts );

         if ( $opts->{disable_decryption} ) {
            $still_encrypted = 1;     # Doesn't get decrypted.
         } else {
            $value = decrypt_value ( $value, $t2, $opts, $file );
         }
      }

      # See if we can expand variables in $value ???
      my $still_variables = 0;
      if ( $opts->{disable_variables} ) {
          $still_variables = ( $value =~ m/${lv}.+${rv}/ ) ? 1 : 0;
      } elsif ( ! $still_encrypted ) {
         ($value, $hide) = expand_variables ( $cfg, $value, $file, $hide, ($lq ? 0 : 1) );
         if ( $hide == -1 ) {
            # $still_encrypted = $still_variables = 1;
            $still_variables = 1;  # Variable(s) points to encrypted data.
         }
      }

      # Export one value to %ENV ... (once set, can't back it out again!)
      $cfg->export_tag_value_to_ENV ( $tag, $value, $hide )  if ($export_flag);

      # Add to the current section in the Advanced::Config object ...

lib/Advanced/Config/Reader.pm  view on Meta::CPAN


=item $boolean = source_file ($config, $def_sct, $new_file, $curr_file)

This is a private method called by I<read_config> to source in the requested
config file and merge the results into the current config file.

If I<$def_sct> is given, it will be the name of the current section that the
sourced in file is to use for it's default unlabeled section.  If the default
section name has been hard coded in the config file, this value overrides it.

The I<$new_file> may contain variables and after they are expanded the
source callback function is called before I<load_config()> is called.
See L<Advanced::Config::lookup_one_variable> for rules on variable expansion.

If I<$new_file> is a relative path, it's a relative path from the location
of I<$curr_file>, not the program's current directory!

If a source callback was set up, it will call it here.

This method will also handle the removal of decryption related options if new
ones weren't provided by the callback function.  See Advanced::Config::Options

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

Returns B<1> if the new file successfully loaded.  Else B<0> if something went
wrong during the load!

=cut

sub source_file
{
   DBUG_ENTER_FUNC (@_);
   my $cfg            = shift;
   my $defaultSection = shift;  # The new default section if not "".
   my $new_file       = shift;  # May contain variables to expand ...
   my $old_file       = shift;  # File we're currently parsing. (has abs path)

   my $rOpts = $cfg->get_cfg_settings ();   # The Read Options ...

   local $global_sections{OVERRIDE} = $defaultSection  if ( $defaultSection );

   my $pcfg = $cfg->get_section ();  # Back to the main/default section ...

   my $file = $new_file = expand_variables ($pcfg, $new_file, undef, undef, 1);

   # Get the full name of the file we're sourcing in ...
   $file = $pcfg->_fix_path ( $file, dirname ( $old_file ) );

   unless ( -f $file && -r _ ) {
      my $msg = "No such file to source in or it's unreadable ( $file )";
      return DBUG_RETURN ( croak_helper ( $rOpts, $msg, 0 ) );
   }

   if ( $cfg->_recursion_check ( $file ) ) {

lib/Advanced/Config/Reader.pm  view on Meta::CPAN



# ==============================================================

=item $name = make_new_section ($config, $section)

This is a private method called by I<read_config> to create a new section
in the L<Advanced::Config> object if a section of that name doesn't already
exist.

The I<$section> name is allowed to contain variables to expand before the
string is used.  But those variables must be defined in the I<main> section.

Returns the name of the section found/created in lower case.

=cut

sub make_new_section
{
   DBUG_ENTER_FUNC (@_);
   my $config   = shift;

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

   if ( $new_name eq "" || $new_name eq $global_sections{DEFAULT} ) {
      if ( $global_sections{DEFAULT} ne $global_sections{OVERRIDE} ) {
         DBUG_PRINT ("OVERRIDE", "Overriding section '%s' with section '%s'",
                     $new_name, $global_sections{OVERRIDE});
         $new_name = $global_sections{OVERRIDE};
      }
   }

   my $pcfg = $config->get_section ();    # Back to the main section ...

   my $val = expand_variables ($pcfg, $new_name, undef, undef, 1);
   $new_name = lc ( $val );

   # Check if the section name is already in use ...
   my $old = $pcfg->get_section ( $new_name );
   if ( $old ) {
      return DBUG_RETURN ( $old->section_name() );
   }

   # Create the new section now that we know it's name is unique ...
   my $scfg = $pcfg->create_section ( $new_name );

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

      }

      unless ( $unbalanced_leading_var_anchor_with_comments ) {
         return DBUG_RETURN ( $tv_pair_flag, $line, $cmts, "", "");
      }
   }

   # ---------------------------------------------------------------------------
   # Corrupted variable definition with variables in the comments ...
   # Boy things are getting difficult to parse.  Reverse the previous variable
   # substitutions until the all variables in the comments are unexpanded again!
   # Does a greedy RegExp to grab the 1st comment string encountered.
   # ---------------------------------------------------------------------------
   if ( $unbalanced_leading_var_anchor_with_comments ) {
      $cmts = "";
      foreach my $l (reverse @data) {
         if ( $l =~ m/\s*${comment}\s*(.*)$/ ) {
            $cmts = $1;
            last  unless ( $cmts =~ m/${has_no_cmt}/ );
            $cmts = "";
         }

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

   $cmts = substr ( $cmts, length ($line) );   # Grab the comments ...
   $cmts =~ s/^\s*${comment}\s*//;             # Remove comment symbol ...

   DBUG_PRINT ("LINE", "Last ditch effort to remove the comment from the line ...");
   DBUG_RETURN ( $tv_pair_flag, $line, $cmts, "", "");
}


# ==============================================================

=item ($v[, $h]) = expand_variables ( $config, $string[, $file[, $sensitive[, trim]]] )

This function takes the provided I<$string> and expands any embedded variables
in this string similar to how it's handled by a Unix shell script.

The optional I<$file> tells which file the string was read in from.

The optional I<$sensitive> when set to a non-zero value is used to disable
B<fish> logging when it's turned on because the I<$string> being passed contains
sensitive information.

The optional I<$trim> tells if you may trim the results before it's returned.

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

       tmp2 = /tmp/work-2
       opt  = 1
       date = 2011-02-03
       logs = ${tmp${opt}}/log-${date}.txt
       date = 2012-12-13

   So when passed "${tmp${opt}}/log-${date}.txt", it would return:
       /tmp/work-1/log-2011-02-03.txt
   And assigned it to B<logs>.

As you can see multiple variable substitutions may be expanded in a single
string as well as nested substitutions.  And when the variable substitution is
done while reading in the config file, all the values used were defined before
the tag was referenced.

Should you call this method after the config file was loaded you get slightly
different results.  In that case the final tag value is used instead and the
2nd date in the above example would have been used in it's place.

See L<Advanced::Config::lookup_one_variable> for more details on how it
evaluates individual variables.

As a final note, if one or more of the referenced variables holds encrypted
values that haven't yet been decrypted, those variables are not resolved.  But
all variables that don't contain encrypted data are resolved.

=cut

# ==============================================================
sub expand_variables
{
   my $config    = shift;           # For the current section of config obj ...
   my $value     = shift;           # The value to parse for variables ...
   my $file      = shift || "";     # The config file the value came from ...
   my $mask_flag = shift || 0;      # Hide/mask sensitive info written to fish?
   my $trim_flag = shift || 0;      # Tells if we should trim the result or not.

   # Only mask ${value} if ${mask_flag} is true ...
   DBUG_MASK_NEXT_FUNC_CALL (1)  if ( $mask_flag );
   DBUG_ENTER_FUNC ( $config, $value, $file, $mask_flag, $trim_flag, @_);

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

                     convert_to_regexp_string ($opts->{variable_right}) );

   # While there are still variables to process ...
   while ( defined $tag ) {
      my ( $val, $mask );
      my $do_mod_lookup = 0;    # Very rarely set to true ...

      # ${tag} and ${mod_tag} will never have the same value ...
      # ${mod_tag} will amost always be undefinded.
      # If both are defined, we'll almost always end up using ${mod_tag} as
      # the real variable to expand!  But we check to be sure 1st.

      ( $val, $mask ) = $config->lookup_one_variable ( $tag );

      # It's extreemly rare to have this "if statement" evalate to true ...
      if ( (! defined $val) && defined $mod_tag ) {
         ( $val, $mask ) = $config->lookup_one_variable ( $mod_tag );

         # -----------------------------------------------------------------
         # If we're using variable modifiers, it doesn't matter if the
         # varible exists or not.  The modifier gets evaluated!

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

   }

   DBUG_RETURN ( $output, $mask_flag );
}


# ==============================================================

=item ($v[, $s]) = apply_modifier ( $config, $value, $tag, $rule, $sub_rule, $file )

This is a helper method to F<expand_variables>.  Not for public use.

This function takes the rule specified by I<$rule> and applies it against
the I<$value> with assistance from the I<$sub_rule>.

It returns the edited I<value> and whether applying the modifier made it
I<sensitive>. (-1 means it's an encrypted value.  -2 means it's the variable
name that resolves to an encrypted value.  0 - Non-sensitive, 1 - Sensitive.)

See L<https://web.archive.org/web/20200309072646/https://wiki.bash-hackers.org/syntax/pe>
for information on how this can work.  This module supports most of the

lib/Advanced/Config/Reader.pm  view on Meta::CPAN

   }

   DBUG_RETURN ( $output, $mask );
}


# ==============================================================

=item @ret[0..7] = parse_for_variables ( $value, $ignore_disable_flag, $rOpts )

This is a helper method to F<expand_variables> and B<parse_line>.

This method parses the I<$value> to see if any variables are defined in it
and returns the information about it.  If there is more than one variable
present in the I<$value>, only the 1st variable/tag to evaluate is returned.

By default, a variable is the tag in the I<$value> between B<${> and B<}>, which
can be overridden with other anchor patterns.  See L<Advanced::Config::Options>
for more details on this.

If you've configured the module to ignore variables, it will never find any.

t/28-sensitive_tests.t  view on Meta::CPAN


         if ( $ans ) {
            dbug_is ( $chk, 1, "Tag '$t' is considered sensitive!  ($v)" );
         } else {
            dbug_is ( $chk, 0, "Tag '$t' is NOT considered sensitive!  ($v)" );
         }
      }
   }

   # --------------------------------------------------------------
   # Section # 2: Now testing which tags have unexpanded variables ...
   #              Only tests tags listed in '00_has_variables'
   # --------------------------------------------------------------
   foreach my $s ( @sections ) {
      dbug_ok (1, "="x50);
      my $lCfg = $cfg2->get_section ( $s );
      dbug_ok (1, "Disabled Variable test for section: " . $s);

      # Get from "main" section, not individual sections
      my $hash_ref2 = $cfg2->get_hash_values ( "00_has_variables" );

t/35-improper_tests.t  view on Meta::CPAN

         if ( $ans ) {
            dbug_is ( $chk, 1, "Tag '$t' is considered sensitive!  ($v)" );
         } else {
            dbug_is ( $chk, 0, "Tag '$t' is NOT considered sensitive!  ($v)" );
         }
      }
      last;   # So only reports on the 1st section ... (so don't have to modify much code)
   }

   # --------------------------------------------------------------
   # Section # 2: Now testing which tags have unexpanded variables ...
   # --------------------------------------------------------------
   foreach my $s ( @sections ) {
      dbug_ok (1, "-"x50);
      my $lCfg = $cfg2->get_section ( $s );
      dbug_ok (1, "Disabled Variable test for section: " . $s);
      my $hash_ref2 = $cfg2->get_hash_values ( "00_has_variables" );

      foreach my $t ( $lCfg->find_tags () ) {
         my $v = $lCfg->get_value ($t);
         my $bool = $lCfg->chk_if_still_uses_variables ($t);



( run in 4.091 seconds using v1.01-cache-2.11-cpan-5b529ec07f3 )