package org.keycloak.protocol.docker;

import org.jboss.logging.Logger;
import org.jboss.resteasy.specimpl.ResponseBuilderImpl;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.events.Errors;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.saml.profile.ecp.authenticator.HttpBasicAuthenticator;
import org.keycloak.representations.docker.DockerAccess;
import org.keycloak.representations.docker.DockerError;
import org.keycloak.representations.docker.DockerErrorResponseToken;

import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Collections;
import java.util.Locale;
import java.util.Optional;

public class DockerAuthenticator extends HttpBasicAuthenticator {
    private static final Logger logger = Logger.getLogger(DockerAuthenticator.class);

    public static final String ID = "docker-http-basic-authenticator";

    @Override
    protected void notValidCredentialsAction(final AuthenticationFlowContext context, final RealmModel realm, final UserModel user) {
        invalidUserAction(context, realm, user.getUsername(), context.getSession().getContext().resolveLocale(user));
    }

    @Override
    protected void nullUserAction(final AuthenticationFlowContext context, final RealmModel realm, final String userId) {
        final String localeString = Optional.ofNullable(realm.getDefaultLocale()).orElse(Locale.ENGLISH.toString());
        invalidUserAction(context, realm, userId, new Locale(localeString));
    }

    @Override
    protected void userDisabledAction(AuthenticationFlowContext context, RealmModel realm, UserModel user) {
        context.getEvent().user(user);
        context.getEvent().error(Errors.USER_DISABLED);

        final DockerError error = new DockerError("UNAUTHORIZED","Invalid username or password.",
                Collections.singletonList(new DockerAccess(context.getAuthenticationSession().getClientNote(DockerAuthV2Protocol.SCOPE_PARAM))));

        context.failure(AuthenticationFlowError.USER_DISABLED, new ResponseBuilderImpl()
                .status(Response.Status.UNAUTHORIZED)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
                .entity(new DockerErrorResponseToken(Collections.singletonList(error)))
                .build());
    }

    /**
     * For Docker protocol the same error message will be returned for invalid credentials and incorrect user name.  For SAML
     * ECP, there is a different behavior for each.
     */
    private void invalidUserAction(final AuthenticationFlowContext context, final RealmModel realm, final String userId, final Locale locale) {
        context.getEvent().user(userId);
        context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);

        final DockerError error = new DockerError("UNAUTHORIZED","Invalid username or password.",
                Collections.singletonList(new DockerAccess(context.getAuthenticationSession().getClientNote(DockerAuthV2Protocol.SCOPE_PARAM))));

        context.failure(AuthenticationFlowError.INVALID_USER, new ResponseBuilderImpl()
                .status(Response.Status.UNAUTHORIZED)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
                .entity(new DockerErrorResponseToken(Collections.singletonList(error)))
                .build());
    }

    @Override
    public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
        return true;
    }
}
