org.hawkular.openshift.auth.BasicAuthenticator.java Source code

Java tutorial

Introduction

Here is the source code for org.hawkular.openshift.auth.BasicAuthenticator.java

Source

/*
 * Copyright 2014-2016 Red Hat, Inc. and/or its affiliates
 * and other 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 org.hawkular.openshift.auth;

import static org.hawkular.openshift.auth.Utils.endExchange;

import static io.undertow.util.Headers.AUTHORIZATION;
import static io.undertow.util.StatusCodes.FORBIDDEN;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.codec.digest.Md5Crypt;
import org.jboss.logging.Logger;

import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;

/**
 * An authentication strategy which allows requests of users listed in an Apaches's server htpasswd file.
 *
 * @author Thomas Segismont
 */
class BasicAuthenticator implements Authenticator {
    private static final Logger log = Logger.getLogger(BasicAuthenticator.class);

    static final String BASIC_PREFIX = "Basic ";
    private static final String HTPASSWD_FILE_SYSPROP = "hawkular-metrics.openshift.htpasswd-file";
    private static final File HTPASSWD_FILE;

    static {
        String htpasswdPath = System.getProperty(HTPASSWD_FILE_SYSPROP);
        if (htpasswdPath == null) {
            HTPASSWD_FILE = new File(System.getProperty("user.home"), ".htpasswd");
        } else {
            HTPASSWD_FILE = new File(htpasswdPath);
        }
    }

    private static final String MD5_PREFIX = "$apr1$";
    private static final String SHA_PREFIX = "{SHA}";

    private final HttpHandler containerHandler;
    private final Map<String, String> users;

    BasicAuthenticator(HttpHandler containerHandler) {
        this.containerHandler = containerHandler;
        users = HTPASSWD_FILE.canRead() ? Collections.unmodifiableMap(readHtpasswdFile()) : Collections.emptyMap();
    }

    private Map<String, String> readHtpasswdFile() {
        Map<String, String> result = new HashMap<String, String>();
        try (BufferedReader reader = new BufferedReader(new FileReader(HTPASSWD_FILE))) {
            reader.lines().forEach(line -> {
                String[] values = line.split(":", 2);
                if (values.length == 2) {
                    result.put(values[0], values[1]);
                }
            });
        } catch (Exception e) {
            log.error("Error trying to setup BasicAuthentication based on an htpasswd file.", e);
        }
        return result;
    }

    @Override
    public void handleRequest(HttpServerExchange serverExchange) throws Exception {
        if (users.isEmpty()) {
            endExchange(serverExchange, FORBIDDEN);
            return;
        }

        String authorizationHeader = serverExchange.getRequestHeaders().getFirst(AUTHORIZATION);
        String usernamePasswordEncoded = authorizationHeader.substring(BASIC_PREFIX.length());
        String usernamePassword = new String(Base64.getDecoder().decode(usernamePasswordEncoded));

        String[] entries = usernamePassword.split(":", 2);
        if (entries.length != 2) {
            endExchange(serverExchange, FORBIDDEN);
            return;
        }

        String username = entries[0];
        String password = entries[1];

        if (users.containsKey(username) && isAuthorized(username, password)) {
            containerHandler.handleRequest(serverExchange);
        } else {
            endExchange(serverExchange, FORBIDDEN);
        }
    }

    private boolean isAuthorized(String username, String password) {
        String storedPassword = users.get(username);
        return (storedPassword.startsWith(MD5_PREFIX) && verifyMD5Password(storedPassword, password))
                || (storedPassword.startsWith(SHA_PREFIX) && verifySHA1Password(storedPassword, password));
    }

    private boolean verifyMD5Password(String storedPassword, String passedPassword) {
        // We send in the password presented by the user and use the stored password as the salt
        // If they match, then the password matches the original non-encrypted stored password
        return Md5Crypt.apr1Crypt(passedPassword, storedPassword).equals(storedPassword);
    }

    private boolean verifySHA1Password(String storedPassword, String passedPassword) {
        //Remove the SHA_PREFIX from the password string
        storedPassword = storedPassword.substring(SHA_PREFIX.length());

        //Get the SHA digest and encode it in Base64
        byte[] digestedPasswordBytes = DigestUtils.sha1(passedPassword);
        String digestedPassword = Base64.getEncoder().encodeToString(digestedPasswordBytes);

        //Check if the stored password matches the passed one
        return digestedPassword.equals(storedPassword);
    }

    @Override
    public void stop() {
    }
}