package WWW::Suffit::Server::Auth;
use strict;
use utf8;

=encoding utf8

=head1 NAME

WWW::Suffit::Server::Auth - The authentication and authorization Suffit API controller

=head1 SYNOPSIS

    use WWW::Suffit::Server::Auth;

=head1 DESCRIPTION

The authentication and authorization Suffit API controller

=head1 METHODS

List of authorization/authentication methods

=head2 is_authorized_api

The API Authorization checker

=head1 HISTORY

See C<Changes> file

=head1 TO DO

See C<TODO> file

=head1 SEE ALSO

L<WWW::Suffit::Server>

=head1 AUTHOR

Serż Minus (Sergey Lepenkov) L<https://www.serzik.com> E<lt>abalama@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright (C) 1998-2023 D&D Corporation. All Rights Reserved

=head1 LICENSE

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

See C<LICENSE> file and L<https://dev.perl.org/licenses/>

=cut

our $VERSION = '1.00';

use Mojo::Base 'Mojolicious::Controller';

use Mojo::JSON qw / true false /;

use WWW::Suffit::Const qw/ TOKEN_EXPIRATION /;

sub is_authorized_api {
    my $self = shift;
    my ($username, $expiration);
    $self->authdb->username(""); # Flash username first!

    # The authorization database not inialized
    unless ($self->authdb->meta("meta.inited")) {
        $self->reply->json_error($self->authdb->error || "E1005: The authorization database is not initialized. Please run \"owl-cli authdb import\" first");
        return;
    }

    # Get authorization data from token (if specified)
    if (my $token = $self->token) {
        my $jwt = $self->jwt;

        # Get payload from JWT
        my $payload = $jwt->decode($token)->payload;
        if ($jwt->error) {
            $self->log->error(sprintf("E1001: Access denied. %s", $jwt->error));
            $self->render(json => {
                status  => false,
                code    => "E1001",
                message => sprintf("Access denied. %s", $jwt->error),
            }, status => 403);
            return;
        }
        $username = $payload->{'usr'};
        $expiration = $payload->{'exp'} || (time + TOKEN_EXPIRATION);

        # Check the token by database
        if ($payload->{'jti'}) {
            unless ($self->authdb->token_check($username, $payload->{'jti'})) {
                if ($self->authdb->error) {
                    $self->log->error($self->authdb->error);
                    $self->render(json => {
                        status  => false,
                        code    => "E0500",
                        message => $self->authdb->error,
                    }, status => 500);
                    return;
                }
                $self->log->error(sprintf("E1002: Access denied. The token %s has been revoked", $payload->{'jti'}));
                $self->render(json => {
                    status  => false,
                    code    => "E1002",
                    message => sprintf("Access denied. The token %s has been revoked", $payload->{'jti'}),
                }, status => 403);
                return;
            }
        }
    } else { # No token specified
        $self->log->error("E1000: Access denied. No token exists");
        $self->render(json => {
            status  => false,
            code    => "E1000",
            message => "E1000: Access denied. No token exists",
        }, status => 403);
        return;
    }

    # Set expiration stash if exists
    $self->stash(expiration => $expiration) if $expiration;

    # Authorization (username is optional)
    if ($username) {
        my $user = $self->authdb->username($username)->authz($username);
        unless ($user) {
            $self->log->error($self->authdb->error || "E1003: Access denied. Session is not authorized for $username");
            $self->render(json => {
                status  => false,
                code    => "E1003",
                message => $self->authdb->error || "Access denied. Session is not authorized",
            }, status => $self->authdb->code);
            return;
        }

        # Stash user data
        $self->stash($user->to_hash);
    }

    # Access (username is optional)
    unless ($self->authdb->access(controller => $self, username => $username)) {
        $self->log->error($self->authdb->error || "E1004: Access denied by realm restrictions");
        $self->render(json => {
            status  => false,
            code    => "E1004",
            message => $self->authdb->error || "Access denied by realm restrictions",
        }, status => $self->authdb->code);
        return;
    }

    # Ok
    return 1;
}


1;

__END__
