#!/usr/bin/perl

eval 'exec /usr/bin/perl  -S $0 ${1+"$@"}'
    if 0; # not running under some shell

# $Id: club_auth 62984 2006-10-06 23:50:57Z nanardon $

use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;

=head1 NAME

club_auth - Perform authentication over rest mandriva system

=head1 SYNOPSIS

    club_auth [LEVEL] [-g group1 [-g ...]]

=head1 OPTIONS

=over 4

=item -g GROUP

People in this group succefully authenticated

=item --noexp

Don't check expiration date for the account

=back

=cut

GetOptions(
    'g=s'    => \my @group,
    'noexp'  => \my $noexpire,
    'help'   => sub { pod2usage(-verbose => 1) },
) or pod2usage();

=head1 DESCRIPTION

This script is designed to ba call by the apache auth_external module. It read
username and passord from L<STDIN>, validate the login then check is user allow
to access to the tree according options given on command line.

If C</var/cache/clubauth> directory exists, data fetch from rest website are
cached into, one file per user.

=cut

# cache is keep 30 minutes (in second)
my $cache_life = 1800;

# Possible value are:
# standard, silver, vip, gold, platinum
# unset value assume we need standard
my %club_level = (
    ALUMINIUM => 1,
    MANDRIVA => 2,
    VIP => 3,
    STANDARD => 4,
    SILVER => 5,
    GOLD => 6,
    PLATINUM => 7,
);

my $count = 0;
# mapping from level_id to perm level
my %perm_levels = map { $club_level{$_} => $count++ } (
    qw(
	ALUMINIUM
	STANDARD
	SILVER
	VIP
	GOLD
	PLATINUM
	MANDRIVA
    )
);

sub perm_level {
	my ($level) = @_;
	# perm order to apply:
	return $perm_levels{$level} || 0;
}

# First arg is the level need
my $need_level = $club_level{uc($ARGV[0] || '')} || $ARGV[0];

my ($login, $password); 
if ($ENV{USER} && defined($ENV{PASS})) {
    ($login, $password) = ($ENV{USER}, $ENV{PASS});
} else {
    $login = <STDIN>;
    $password = <STDIN>;
    chomp($login); chomp($password);
}

sub  get_data_rest {
	use LWP::UserAgent ();
	use HTTP::Request ();
	# Need to perform http_s_, so making mandatory here:
	use Crypt::SSLeay;
	use HTML::Entities;

	my $ua = LWP::UserAgent->new;
	my $http = HTTP::Request->new(
		POST => 'https://my.mandriva.com/rest/authenticate.php',
	);
	$http->content_type('application/x-www-form-urlencoded');
	$http->content(sprintf(
		'username=%s&password=%s&return=userData&use_cs4_fix=1',
		$login, $password,
	));

	my $res = $ua->request($http);
	if ($res->is_success()) {
        use XML::Simple;
        my $ref = XMLin($res->content());
		if (-d '/var/cache/clubauth') {
			my $filename = unpack('H*', $login);
			if (open(my $h, '>', "/var/cache/clubauth/$filename")) {
				printf $h 
                    "%s\n%d\n", 
                    crypt($password, '$1$clubauth'),
                    $ref->{code},
                    ;
				print $h $res->content();
				close($h);
			}
		}
		return $ref;
	}
	return;
}

sub get_data_cache {
	-d '/var/cache/clubauth' or return;
	my $filename = unpack('H*', $login);
	my @stat = stat("/var/cache/clubauth/$filename") or return;
	if (@stat) {
		if ($stat[9] > scalar(time) - $cache_life) {
			open(my $h, '<', "/var/cache/clubauth/$filename") or return;
			my $cached_password = <$h>;
			chomp($cached_password);
			my $passwd_mismatch;
			if (crypt($password, '$1$clubauth') ne $cached_password) {
				$passwd_mismatch = 1;
			}
			my $ref;
            $ref->{code} = <$h>;
            chomp($ref->{code});
	    my $rest_data = '';
	    while (<$h>) {
		    $rest_data .= $_;
	    }
	    close($h);
		    if (($passwd_mismatch && !$ref->{code}) || (!$passwd_mismatch && $ref->{code})) {
			return { code => 1 };
		    } elsif ($passwd_mismatch && $ref->{code}) {
			return;
		    } else {
			use XML::Simple;
			return XMLin($rest_data) || {};
		    }
	    } else {
	    	unlink("/var/cache/clubauth/$filename"); # cleaning
	    }
	}
	return;
}

my $ref = get_data_cache() || get_data_rest();

if ($ref) {
    # We failed to retrieve info:
    if ($ref->{code}) {
	    exit($ref->{code});
    }

    # We can authenticated over a group:
    if (@group) {
	    use Date::Parse;
	    foreach my $g (@group) {
            if ($noexpire) {
                $ref->{data}{groups}{$g} and exit(0);
            } else {
		        $ref->{data}{groups}{$g}{'dateExpires'} or next;
		        my $time = str2time($ref->{data}{groups}{$g}{'dateExpires'}) or next;
		        exit(0) if($time >= time());
            }
	    }
    }

    if($need_level) {
    	exit(perm_level($ref->{data}{club_level}) < perm_level($need_level));
    }
	exit(0);
} else {
    exit(1);
}

__END__

=head1 COPYRIGHT AND LICENSE

(c) Mandriva - 2007, 2008, 2009

This script is under Perl License.

=head1 AUTHOR

Olivier Thauvin <nanardon@mandriva.org>

=cut
