package WebAPI::DBIC::Resource::Role::DBICAuth;
$WebAPI::DBIC::Resource::Role::DBICAuth::VERSION = '0.004002';

use Carp qw(confess);
use Try::Tiny;

use WebAPI::DBIC::Util qw(create_header);

use Moo::Role;


requires 'set';
requires 'http_auth_type';

sub connect_schema_as { # XXX sub rather than method?
    my ($self, $user, $pass) = @_;
    $_[2] = '...'; # hide password from stack trace

    my $schema = $self->set->result_source->schema;
    my $ci = $schema->storage->connect_info;
    my ($ci_dsn, $ci_user, $ci_pass, $ci_attr) = @$ci;

    # ok if we're currently using the right auth
    return 1 if defined $ci_user and $user eq $ci_user
            and defined $ci_pass and $pass eq $ci_pass;

    # try to connect with the user supplied credentials
    my $newschema = $schema->clone->connect($ci_dsn, $user, $pass, $ci_attr);
    my $err;
    try { $newschema->storage->dbh }
    catch {
        # XXX we need to differentiate between auth errors and other problems
        warn "Error connecting to $ci_dsn: $_\n";
        $err = $_;
    };
    return 0 if $err;

    # we connected ok, so update resultset to use new connection
    # XXX Is this sane and safe?
    $self->set->result_source->schema($newschema);

    return 1;
}


sub is_authorized {
    my ($self, $auth_header) = @_;

    my $http_auth_type = $self->http_auth_type || '';
    if ($http_auth_type =~ /^(none|disabled)$/) {
        # This role was included in the resource, so auth was desired, yet auth
        # has been specified. That seems worthy of a warning.
        # 'none' gives a warning, but 'disabled' is silent.
        (my $name = $self->request->path) =~ s:/\d+$::;
        warn "HTTP authentication configured but not enabled for $name\n"
            if $http_auth_type ne 'disabled'
            and not our $warn_once->{"http_auth_type $name"}++;
        return 1
    }
    elsif ($http_auth_type eq 'Basic') {

        # https://metacpan.org/pod/DBIx::Class::Storage::DBI#connect_info
        my $ci = $self->set->result_source->schema->storage->connect_info;
        # extract the dsn (doesn't handle $ci->[0] being a code ref)
        my $dsn = (ref $ci->[0]) ? $ci->[0]->{dsn} : $ci->[0];
        confess "Can't determine DSN to use as auth realm from @$ci"
            if !$dsn or ref $dsn;

        my $auth_realm = "Insecure unless https! - $dsn"; # XXX get via a method
        if ( $auth_header ) {
            return 1 if $self->connect_schema_as($auth_header->username, $auth_header->password);
        }
        return create_header( 'WWWAuthenticate' => [ 'Basic' => ( realm => $auth_realm ) ] );
    }

    die "Unsupported value for http_auth_type: $http_auth_type";
}


1;

__END__

=pod

=encoding UTF-8

=head1 NAME

WebAPI::DBIC::Resource::Role::DBICAuth

=head1 VERSION

version 0.004002

=head1 NAME

WebAPI::DBIC::Resource::Role::DBICAuth - methods for authentication and authorization

=head1 AUTHOR

Tim Bunce <[email protected]>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2015 by Tim Bunce.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut