<?php

namespace NoccyLabs\SimpleJwt\Validator;

use NoccyLabs\SimpleJwt\JwtToken;
use NoccyLabs\SimpleJwt\Key\JwtPlaintextKey;

class JwtValidatorTest extends \PhpUnit\Framework\TestCase
{

    public function testValidTokensShouldPassWithDefaultConfiguration()
    {
        $key = new JwtPlaintextKey("key");
        $token = new JwtToken($key);
        
        $validator = new JwtValidator();
        $valid = $validator->validateToken($token);
        $this->assertEquals(true, $valid);
    }

    public function testExpiredTokensShouldFailWithException()
    {
        $key = new JwtPlaintextKey("key");
        $token = new JwtToken($key);
        $token->header->set("exp", 0);

        $token = new JwtToken($key, $token->getSignedToken()); 
        
        $validator = new JwtValidator();
        $this->expectException(JwtTokenException::class);
        $valid = $validator->validateToken($token);
    }

    /**
     * @dataProvider tokenGenerator
     */
    public function testPinningIssuer($issuer,$audience,$key,$token)
    {
        $goodIssuer = "a-dom.tld";
        $jwtKey = new JwtPlaintextKey($key);
        $jwtToken = new JwtToken($jwtKey, $token);

        $validator = new JwtValidator();
        $validator->requireIssuer($goodIssuer);
        if ($goodIssuer != $issuer) {
            $this->expectException(JwtTokenException::class);
        }
        $valid = $validator->validateToken($jwtToken);
        if ($goodIssuer == $issuer) {
            $this->assertTrue($valid);
        }
    }

    /**
     * @dataProvider tokenGenerator
     */
    public function testPinningAudience($issuer,$audience,$key,$token)
    {
        $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ];
        $jwtKey = new JwtPlaintextKey($key);
        $jwtToken = new JwtToken($jwtKey, $token);

        $validator = new JwtValidator();
        $validator->requireAudience($goodAudience);
        if (!in_array($audience, $goodAudience)) {
            $this->expectException(JwtTokenException::class);
        }
        $valid = $validator->validateToken($jwtToken);
        if (in_array($audience, $goodAudience)) {
            $this->assertTrue($valid);
        }
    }

    /**
     * @dataProvider tokenGenerator
     */
    public function testPinningBoth($issuer,$audience,$key,$token)
    {
        $goodIssuer = "a-dom.tld";
        $goodAudience = [ "a-dom.tld", "app.a-dom.tld" ];
        $jwtKey = new JwtPlaintextKey($key);
        $jwtToken = new JwtToken($jwtKey, $token);

        $validator = new JwtValidator();
        $validator->requireIssuer($goodIssuer);
        $validator->requireAudience($goodAudience);
        if (($goodIssuer != $issuer) || (!in_array($audience, $goodAudience))) {
            $this->expectException(JwtTokenException::class);
        }
        $valid = $validator->validateToken($jwtToken);
        if (($goodIssuer == $issuer) && (in_array($audience, $goodAudience))) {
            $this->assertTrue($valid);
        }
    }

    public function tokenGenerator()
    {
        $keyrand = function () {
            return substr(sha1(microtime(true).rand(0,65535)), 5, 10);
        };
        $token = function ($head,$claims,$key) {
            $jwtKey = new JwtPlaintextKey($key);
            $tok = new JwtToken($jwtKey);
            $tok->header->setAll($head);
            $tok->claims->setAll($claims);
            return $tok->getSignedToken();
        };
        $row = function ($iss, $aud, array $claims) use ($keyrand, $token) {
            $key = $keyrand();
            $jwtKey = new JwtPlaintextKey($key);
            return [
                $iss,
                $aud,
                $key,
                $token(['iss'=>$iss, 'aud'=>$aud], $claims, $key),
            ];
        };

        return [
            $row("a-dom.tld", "a-dom.tld", []),
            $row("b-dom.tld", "a-dom.tld", []),
            $row("b-dom.tld", "b-dom.tld", []),
            $row("a-dom.tld", "app.a-dom.tld", []),
            $row("a-dom.tld", "app.b-dom.tld", []),
            $row("", "app.b-dom.tld", []),
        ];

    }

}