Apache2-AuthEnv

 view release on metacpan or  search on metacpan

lib/Apache2/AuthEnv.pm  view on Meta::CPAN

		name		=> 'AuthEnvSet',
		args_how	=> Apache2::Const::TAKE2,
		errmsg		=> 'AuthEnvSet EnvVar Format',
	},
	{
		name		=> 'AuthEnvChange',
		args_how	=> Apache2::Const::TAKE2,
		errmsg		=> 'AuthEnvChange EnvVar <subsitution>'
	},
	{
		name		=> 'AuthEnvDenial',
		args_how	=> Apache2::Const::TAKE1,
		errmsg		=> 'AuthEnvDenial <UNAUTHORISED|UNAUTHORIZED|NOT_FOUND|FORBIDDEN>'
	},
	{
		name		=> 'AuthEnvLogInfo',
		args_how	=> Apache2::Const::FLAG,
		errmsg		=> 'AuthEnvLogInfo On/Off',
	},
	{
		name		=> 'AuthEnvLogDebug',
		args_how	=> Apache2::Const::FLAG,
		errmsg		=> 'AuthEnvLogInfo On/Off',
	},
);

# Register the directives.
Apache2::Module::add(__PACKAGE__, \@directives);

# Debugging only.
sub debug { 1; }

# errors.
sub err { warn @_; }

# Log information
sub info { 1; }

# Create an object; not used by mod_perl2
sub new
{
        # Create an object.
	my $this = shift;
	my $class = ref($this) || $this;
	my $self = { };
	bless $self, $class;

	$self;
}

###################### Directives ###########################################

# Set the environment variable to use for authentication
# and set the system to authenticate and authorise.
sub AuthEnvUser
{
	my ($cfg, $parms, $fmt, @args) = @_;

	my $line = join(':', $parms->directive->filename, $parms->directive->line_num);

	# Check that the format contains something to expand.
	# Warn if it's fixed.
	unless ($fmt =~ /%\{.*\}/)
	{
		# NB the request object is not available when called in 
		# global config files (eg httpd.conf).
  		err("AuthEnvUser format '$fmt' has no expansion at $line");

		#return Apache2::Const::HTTP_FORBIDDEN;
	}

	# Loading the configuration handles for auth*.
	# This can be done anywhere so there shouldnever be a problem.
	eval {
            $parms->add_config([
		'PerlAuthenHandler Apache2::AuthEnv::authenticate',
		'PerlAuthzHandler  Apache2::AuthEnv::authorise',
	]);
	};
 	warn "$line: $@" if ($@);

	# Force auth* stages to be done by loading the configuration.
	# May not be allowed in this part of the httpd conf files.
	# So trap!
	eval {
		$parms->add_config([
			'AuthType AuthEnv',
			'Require valid-user',
		]);
	};

	# Should never be a problem because the directive is 
	# restricted to location, directory and .htaccess only.
	# Trap the error.
	if ($@) {
		if ($@ =~ /not allowed/i)
		{
			# Directive not allowed in this part of httpd configuration.
  			warn "AuthEnvUser not allowed here at $line";
		}
		else
		{
			# Unknown failure.
  			warn "AuthEnvUser: $@ at $line";
		}

		exit 2;
	}

	# Save value for user name format.
	$cfg->{AuthEnvUser} = $fmt;

	# Make sure the the user gets set later.
	push @{$cfg->{set}}, ['set', 'REMOTE_USER', $fmt];

	# Initialise the authorise rule list.
	$cfg->{authorise} = ();

	1;
}

lib/Apache2/AuthEnv.pm  view on Meta::CPAN

sub AuthEnvLogDebug
{
	my ($cfg, $parms, $onoff) = @_;

	$cfg->{LogDebug} = $onoff;

	1;
}

###################### End of directives #####################################

# Merge configuration objects together so the the various 
# Apache config files override each other.
sub merge
{
        my ($base, $add) = @_;

	my $merged = new Apache2::AuthEnv;

	# Merge environment variables to set.
	$merged->{set} = $base->{set};
	push @{$merged->{set}}, @{$add->{set}};
	delete $base->{set};
	delete $add->{set};

	for my $k (keys %$base) { $merged->{$k} = $base->{$k}; } 
	for my $k (keys %$add)  { $merged->{$k} = $add->{$k};  }

	$merged;
}

# Turn on custom merging.
sub DIR_MERGE    { merge(@_) }
sub SERVER_MERGE { merge(@_) }


# Fill out a sub-format with the correct values.
# Take a context ($r), a format of environment variables (with optional default) and 
# a fail reference.
# Return the value of the first environment variable that exists, or the default if specified
# or '' and increament the failure variable reference.
sub fillout
{
	my ($r, $fmt, $fail) = @_;

	debug("Expanding '$fmt' for URL ", $r->uri);

	# Isolate the default value.
	my $default = ($fmt =~ s/:(\w*)$//) ? $1 : undef;

	# Run though each environment valriable in turn.
	for my $e (split(/\|/, $fmt))
	{
		# return value if it exists.
		return $r->subprocess_env($e) if defined($r->subprocess_env($e));
	}

	# Otherwise return the default value.
	return $default if defined $default;

	info "Failed to expand '$fmt' for URL ", $r->uri;

	# Failed.
 	$$fail++;

	'';
}

# Look a key up in the MLDBM database, with a function that can be cached.
sub dblookup2
{
	my ($file, $var) = @_;
	##warn("db key '$var' in file '$file'");

	my $null = freeze {};

	return $null unless defined $file;

	my $db = tie my %data,  'MLDBM', 
		-Filename => $file, 
		-Flags => DB_RDONLY,
	;

	unless ($db)
	{
		err("Cannot read database '$file' failed ($!) ");
		return $null;
	}

	# Side step any taint issues.
	# The datbase is a valid file.
	$db->RemoveTaint(1);

	# Return nothing if there is no entry.
	return $null unless exists $data{$var};

	# Return frozen data.
	freeze $data{$var};
}

# Wrap the lookup function.
tie my %mcache => 'Memoize::Expire',
        LIFETIME => 5,    # In seconds
;
memoize 'dblookup2', SCALAR_CACHE => [HASH => \%mcache ], LIST_CACHE => 'FAULT', ;

# This is a wrapper to manage the unthawing process correctly.
sub dblookup
{
	my $user = dblookup2(@_);
	$user = thaw $user;
}

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

# NB There is almost no environment to speak of at this time!

# Authenticate a user based on the presence of environemnt variables.
# Fail to authenticate if a environment variable doesn't exist.
# Promote environment variables in format to REMOTE_USER.
sub authenticate
{
	my ($r) = @_;

	# recover configuration.
        my $cfg = Apache2::Module::get_config(__PACKAGE__, $r->server, $r->per_dir_config);

	# Check that we are using the right AuthType directive.
	my $type = __PACKAGE__; $type =~ s/^.*:://;
	if ($r->auth_type ne $type)
	{
		err("Wrong authentication Type ", $r->auth_type);
		return Apache2::Const::HTTP_UNAUTHORIZED;
	}
	unless (defined $cfg->{AuthEnvUser})
	{
		err("AuthEnvUser not used! ", $r->auth_type);
		return Apache2::Const::HTTP_UNAUTHORIZED;
	}

	# set logging on or off.
	if (exists $cfg->{LogInfo} && $cfg->{LogInfo})
	{
		# info on
		no warnings;
		eval 'sub info { warn @_; };';
	}
	else
	{
		# info off
		no warnings;
		eval 'sub info { 1; };';
	}
	
	# set debugging on or off.
	if (exists $cfg->{LogDebug} && $cfg->{LogDebug})
	{
		# debug on
		no warnings;
		eval 'sub debug { warn @_; };';
	}
	else
	{
		# info off
		no warnings;
		eval 'sub debug { 1; };';
	}

	# Import CGI environment.
	$r->subprocess_env unless $r->is_perl_option_enabled('SetupEnv');

        # expand $AuthEnvUser format; fail if a variable doesn't
        # not exist.

	# Check that AuthEnvUser is set.
	return Apache2::Const::HTTP_UNAUTHORIZED unless exists $cfg->{AuthEnvUser};

	# Set the AE version environment.
	$r->subprocess_env('HTTP_AE_VERSION', $VERSION);

	# Set the environment and the REMOTE_USER along the way.
	for my $s (@{$cfg->{set}})
	{
		my ($act, $v, $f) = @$s;

		# Set an environment variable.
	
		if ($act eq 'dbimport')
		{
			my ($act, $prefix, $file, $var) = @$s;
			my $fail = 0; # count non-existant variables.
			$var =~ s/%\{([^\}]+)\}/&fillout($r, $1, \$fail)/gxe;
			next if $fail;

			# Load user data.
			my $user = dblookup($file, $var);

			# Load the environment.
			for my $k (keys %$user)
			{
				debug("db env key '$k' for URL ", $r->uri);
				$r->subprocess_env($prefix . uc($k), $user->{$k});
			}
		}
		elsif ($act eq 'set')
		{
			my $fail = 0; # count non-existant variables.

			#debug($r->uri, ": change '$f'");

			$f =~ s/%\{([^\}]+)\}/&fillout($r, $1, \$fail)/gxe;

			# something wasn't defined.
			return Apache2::Const::HTTP_UNAUTHORIZED if $fail;

			$r->subprocess_env($v, $f);
		}
		# Change an environment variable.
		elsif ($act eq 'change')
		{
			my $val = $r->subprocess_env($v);

			# Run the modification in a safe environment.
			my $cpt = new Safe;
			${$cpt->varglob('val')} = $val;
			$cpt->reval("\$val =~ $f");

			if ($@)
			{
				# failure to run.
				err("change '$f' failed ($@) ", $r->uri);
				return Apache2::Const::HTTP_UNAUTHORIZED;



( run in 1.985 second using v1.01-cache-2.11-cpan-97f6503c9c8 )