/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package io.undertow.server.security;

import io.undertow.testutils.category.UnitTest;
import io.undertow.security.idm.DigestAlgorithm;
import io.undertow.security.impl.DigestAuthorizationToken;
import io.undertow.security.impl.DigestQop;
import org.junit.Test;
import org.junit.experimental.categories.Category;

import java.util.EnumMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;

/**
 * Test case to test the parsing of the Authorization header for Digest requests.
 *
 * The RFC defines which values are quoted and which ones are not, however different implementations interpret this differently.
 * This test case tests different headers generated by supported browsers.
 *
 * Ordering of the values is not important, however the construction of the Map of expected values does match the header being
 * tested for readability.
 *
 * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a>
 */
@Category(UnitTest.class)
public class ParseDigestAuthorizationTokenTestCase {

    private void doTest(final String header, final Map<DigestAuthorizationToken, String> expected) {
        Map<DigestAuthorizationToken, String> parsedHeader = DigestAuthorizationToken.parseHeader(header);

        assertEquals(expected, parsedHeader);
    }

    @Test
    public void testChrome_22() {
        final String header = "username=\"userTwo\", realm=\"Digest_Realm\", nonce=\"Yxmkh5liIOYNMTM1MTUyNjQzMTE4NJziT7YLEOEJ4QEN1py4Yog=\", uri=\"/\", algorithm=MD5, response=\"5b26e00233607e8a714cd1d910692e08\", opaque=\"00000000000000000000000000000000\", qop=auth, nc=00000001, cnonce=\"8c008c8ce43dc0a7\"";

        Map<DigestAuthorizationToken, String> expected = new EnumMap<>(DigestAuthorizationToken.class);
        expected.put(DigestAuthorizationToken.USERNAME, "userTwo");
        expected.put(DigestAuthorizationToken.REALM, "Digest_Realm");
        expected.put(DigestAuthorizationToken.NONCE, "Yxmkh5liIOYNMTM1MTUyNjQzMTE4NJziT7YLEOEJ4QEN1py4Yog=");
        expected.put(DigestAuthorizationToken.DIGEST_URI, "/");
        expected.put(DigestAuthorizationToken.ALGORITHM, DigestAlgorithm.MD5.getToken());
        expected.put(DigestAuthorizationToken.RESPONSE, "5b26e00233607e8a714cd1d910692e08");
        expected.put(DigestAuthorizationToken.OPAQUE, "00000000000000000000000000000000");
        expected.put(DigestAuthorizationToken.MESSAGE_QOP, DigestQop.AUTH.getToken());
        expected.put(DigestAuthorizationToken.NONCE_COUNT, "00000001");
        expected.put(DigestAuthorizationToken.CNONCE, "8c008c8ce43dc0a7");

        doTest(header, expected);
    }

    @Test
    public void testCurl_7() {
        final String header = "username=\"userTwo\", realm=\"Digest_Realm\", nonce=\"5CgZ39vhie0NMTM1MTUyNDc4ODkwNMwr6sWKVSGfhXB4jBtkupY=\", uri=\"/\", cnonce=\"MTYwOTQ4\", nc=00000001, qop=\"auth\", response=\"c3c1ce9945a0c36d54860eda7846018b\", opaque=\"00000000000000000000000000000000\", algorithm=\"MD5\"";

        Map<DigestAuthorizationToken, String> expected = new EnumMap<>(DigestAuthorizationToken.class);
        expected.put(DigestAuthorizationToken.USERNAME, "userTwo");
        expected.put(DigestAuthorizationToken.REALM, "Digest_Realm");
        expected.put(DigestAuthorizationToken.NONCE, "5CgZ39vhie0NMTM1MTUyNDc4ODkwNMwr6sWKVSGfhXB4jBtkupY=");
        expected.put(DigestAuthorizationToken.DIGEST_URI, "/");
        expected.put(DigestAuthorizationToken.CNONCE, "MTYwOTQ4");
        expected.put(DigestAuthorizationToken.NONCE_COUNT, "00000001");
        expected.put(DigestAuthorizationToken.MESSAGE_QOP, DigestQop.AUTH.getToken());
        expected.put(DigestAuthorizationToken.RESPONSE, "c3c1ce9945a0c36d54860eda7846018b");
        expected.put(DigestAuthorizationToken.OPAQUE, "00000000000000000000000000000000");
        expected.put(DigestAuthorizationToken.ALGORITHM, DigestAlgorithm.MD5.getToken());

        doTest(header, expected);
    }

    @Test
    public void testFirefox_16() {
        final String header = "username=\"userOne\", realm=\"Digest_Realm\", nonce=\"nBhFxtSS6rkNMTM1MTUyNjE2MjgyNWA/xW/LOH53vhXGq/2B/yQ=\", uri=\"/\", algorithm=MD5, response=\"b0adb1025da2de0d16f44131858bad6f\", opaque=\"00000000000000000000000000000000\", qop=auth, nc=00000001, cnonce=\"8127726535363b07\"";

        Map<DigestAuthorizationToken, String> expected = new EnumMap<>(DigestAuthorizationToken.class);
        expected.put(DigestAuthorizationToken.USERNAME, "userOne");
        expected.put(DigestAuthorizationToken.REALM, "Digest_Realm");
        expected.put(DigestAuthorizationToken.NONCE, "nBhFxtSS6rkNMTM1MTUyNjE2MjgyNWA/xW/LOH53vhXGq/2B/yQ=");
        expected.put(DigestAuthorizationToken.DIGEST_URI, "/");
        expected.put(DigestAuthorizationToken.ALGORITHM, DigestAlgorithm.MD5.getToken());
        expected.put(DigestAuthorizationToken.RESPONSE, "b0adb1025da2de0d16f44131858bad6f");
        expected.put(DigestAuthorizationToken.OPAQUE, "00000000000000000000000000000000");
        expected.put(DigestAuthorizationToken.MESSAGE_QOP, DigestQop.AUTH.getToken());
        expected.put(DigestAuthorizationToken.NONCE_COUNT, "00000001");
        expected.put(DigestAuthorizationToken.CNONCE, "8127726535363b07");

        doTest(header, expected);
    }

    @Test
    public void testOpera_12() {
        final String header = "username=\"userOne\", realm=\"Digest_Realm\", uri=\"/\", algorithm=MD5, nonce=\"D2floAc+FhkNMTM1MTUyMzY2ODc4Mhbi2Zrcuv1lvdgEaPXa+bg=\", cnonce=\"v722VYJEeG28C3SoXS8BEWThGHPDOlXgUCCts70i7Fc=\", opaque=\"00000000000000000000000000000000\", qop=auth, nc=00000001, response=\"8106a5d19bc67982527cbb576658f9d6\"";

        Map<DigestAuthorizationToken, String> expected = new EnumMap<>(DigestAuthorizationToken.class);
        expected.put(DigestAuthorizationToken.USERNAME, "userOne");
        expected.put(DigestAuthorizationToken.REALM, "Digest_Realm");
        expected.put(DigestAuthorizationToken.DIGEST_URI, "/");
        expected.put(DigestAuthorizationToken.ALGORITHM, DigestAlgorithm.MD5.getToken());
        expected.put(DigestAuthorizationToken.NONCE, "D2floAc+FhkNMTM1MTUyMzY2ODc4Mhbi2Zrcuv1lvdgEaPXa+bg=");
        expected.put(DigestAuthorizationToken.CNONCE, "v722VYJEeG28C3SoXS8BEWThGHPDOlXgUCCts70i7Fc=");
        expected.put(DigestAuthorizationToken.OPAQUE, "00000000000000000000000000000000");
        expected.put(DigestAuthorizationToken.MESSAGE_QOP, DigestQop.AUTH.getToken());
        expected.put(DigestAuthorizationToken.NONCE_COUNT, "00000001");
        expected.put(DigestAuthorizationToken.RESPONSE, "8106a5d19bc67982527cbb576658f9d6");

        doTest(header, expected);
    }

    @Test
    public void testHttpClient_55() {
        final String header = "username=\"userTwo\", realm=\"Digest_Realm\", nonce=\"Yxmkh5liIOYNMTM1MTUyNjQzMTE4NJziT7YLEOEJ4QEN1py4Yog=\", uri=\"/\", response=\"5b26e00233607e8a714cd1d910692e08\", qop=auth, nc=00000001, cnonce=\"8c008c8ce43dc0a7\", rspauth=\"3b3473383bf0d182a6c6653e88ecc2e7\", algorithm=MD5, opaque=\"00000000000000000000000000000000\"";

        Map<DigestAuthorizationToken, String> expected = new EnumMap<>(DigestAuthorizationToken.class);
        expected.put(DigestAuthorizationToken.USERNAME, "userTwo");
        expected.put(DigestAuthorizationToken.REALM, "Digest_Realm");
        expected.put(DigestAuthorizationToken.NONCE, "Yxmkh5liIOYNMTM1MTUyNjQzMTE4NJziT7YLEOEJ4QEN1py4Yog=");
        expected.put(DigestAuthorizationToken.DIGEST_URI, "/");
        expected.put(DigestAuthorizationToken.ALGORITHM, DigestAlgorithm.MD5.getToken());
        expected.put(DigestAuthorizationToken.RESPONSE, "5b26e00233607e8a714cd1d910692e08");
        expected.put(DigestAuthorizationToken.OPAQUE, "00000000000000000000000000000000");
        expected.put(DigestAuthorizationToken.MESSAGE_QOP, DigestQop.AUTH.getToken());
        expected.put(DigestAuthorizationToken.NONCE_COUNT, "00000001");
        expected.put(DigestAuthorizationToken.CNONCE, "8c008c8ce43dc0a7");
        expected.put(DigestAuthorizationToken.RESPONSE_AUTH, "3b3473383bf0d182a6c6653e88ecc2e7");

        doTest(header, expected);
    }
}
