/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License;
 * you may not use this file except in compliance with the Elastic License.
 */
package org.elasticsearch.xpack.security.rest.action;

import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.SecurityIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.test.rest.yaml.ObjectPath;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.junit.BeforeClass;

import java.util.List;

import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

public class RestAuthenticateActionTests extends SecurityIntegTestCase {

    private static boolean anonymousEnabled;

    @BeforeClass
    public static void maybeEnableAnonymous() {
        anonymousEnabled = randomBoolean();
    }

    @Override
    protected Settings nodeSettings(int nodeOrdinal) {
        Settings.Builder builder = Settings.builder()
                .put(super.nodeSettings(nodeOrdinal))
                .put(NetworkModule.HTTP_ENABLED.getKey(), true);

        if (anonymousEnabled) {
            builder.put(AnonymousUser.USERNAME_SETTING.getKey(), "anon")
                   .putArray(AnonymousUser.ROLES_SETTING.getKey(), SecuritySettingsSource.DEFAULT_ROLE, "foo")
                   .put(AuthorizationService.ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.getKey(), false);
        }
        return builder.build();
    }

    public void testAuthenticateApi() throws Exception {
        Response response = getRestClient().performRequest("GET", "/_xpack/security/_authenticate",
                new BasicHeader("Authorization", basicAuthHeaderValue(SecuritySettingsSource.DEFAULT_USER_NAME,
                        new SecureString(SecuritySettingsSource.DEFAULT_PASSWORD.toCharArray()))));
        assertThat(response.getStatusLine().getStatusCode(), is(200));
        ObjectPath objectPath = ObjectPath.createFromResponse(response);
        assertThat(objectPath.evaluate("username").toString(), equalTo(SecuritySettingsSource.DEFAULT_USER_NAME));
        @SuppressWarnings("unchecked")
        List<String> roles = (List<String>) objectPath.evaluate("roles");
        assertThat(roles.size(), is(1));
        assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE));
    }

    public void testAuthenticateApiWithoutAuthentication() throws Exception {
        try {
            Response response = getRestClient().performRequest("GET", "/_xpack/security/_authenticate");
            if (anonymousEnabled) {
                assertThat(response.getStatusLine().getStatusCode(), is(200));
                ObjectPath objectPath = ObjectPath.createFromResponse(response);
                assertThat(objectPath.evaluate("username").toString(), equalTo("anon"));
                @SuppressWarnings("unchecked")
                List<String> roles = (List<String>) objectPath.evaluate("roles");
                assertThat(roles.size(), is(2));
                assertThat(roles, contains(SecuritySettingsSource.DEFAULT_ROLE, "foo"));
            } else {
                fail("request should have failed");
            }
        } catch(ResponseException e) {
            if (anonymousEnabled) {
                fail("request should have succeeded");
            } else {
                assertThat(e.getResponse().getStatusLine().getStatusCode(), is(401));
            }
        }
    }
}
