All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glowroot.local.ui.HttpSessionManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013-2015 the original author or authors.
 *
 * 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.glowroot.local.ui;

import java.io.IOException;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Nullable;

import org.glowroot.shaded.google.common.base.Charsets;
import org.glowroot.shaded.google.common.collect.Maps;
import org.glowroot.shaded.netty.handler.codec.http.DefaultFullHttpResponse;
import org.glowroot.shaded.netty.handler.codec.http.FullHttpRequest;
import org.glowroot.shaded.netty.handler.codec.http.FullHttpResponse;
import org.glowroot.shaded.netty.handler.codec.http.HttpRequest;
import org.glowroot.shaded.netty.handler.codec.http.HttpResponse;
import org.glowroot.shaded.netty.handler.codec.http.cookie.Cookie;
import org.glowroot.shaded.netty.handler.codec.http.cookie.DefaultCookie;
import org.glowroot.shaded.netty.handler.codec.http.cookie.ServerCookieDecoder;
import org.glowroot.shaded.netty.handler.codec.http.cookie.ServerCookieEncoder;
import org.glowroot.shaded.slf4j.Logger;
import org.glowroot.shaded.slf4j.LoggerFactory;

import org.glowroot.common.Clock;
import org.glowroot.config.AnonymousAccess;
import org.glowroot.config.ConfigService;

import static org.glowroot.shaded.netty.handler.codec.http.HttpHeaders.Names.COOKIE;
import static org.glowroot.shaded.netty.handler.codec.http.HttpHeaders.Names.SET_COOKIE;
import static org.glowroot.shaded.netty.handler.codec.http.HttpResponseStatus.INTERNAL_SERVER_ERROR;
import static org.glowroot.shaded.netty.handler.codec.http.HttpResponseStatus.OK;
import static org.glowroot.shaded.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static java.util.concurrent.TimeUnit.MINUTES;

class HttpSessionManager {

    private static final Logger logger = LoggerFactory.getLogger(HttpSessionManager.class);

    private final ConfigService configService;
    private final Clock clock;
    private final LayoutService layoutJsonService;

    private final SecureRandom secureRandom = new SecureRandom();
    private final Map adminSessionExpirations = Maps.newConcurrentMap();
    private final Map readOnlySessionExpirations = Maps.newConcurrentMap();

    HttpSessionManager(ConfigService configService, Clock clock, LayoutService layoutJsonService) {
        this.configService = configService;
        this.clock = clock;
        this.layoutJsonService = layoutJsonService;
    }

    FullHttpResponse login(FullHttpRequest request, boolean admin) throws IOException {
        boolean success;
        String password = request.content().toString(Charsets.ISO_8859_1);
        String existingPasswordHash;
        if (admin) {
            existingPasswordHash = configService.getUserInterfaceConfig().adminPasswordHash();
        } else {
            existingPasswordHash = configService.getUserInterfaceConfig().readOnlyPasswordHash();
        }
        try {
            success = validatePassword(password, existingPasswordHash);
        } catch (GeneralSecurityException e) {
            logger.error(e.getMessage(), e);
            return new DefaultFullHttpResponse(HTTP_1_1, INTERNAL_SERVER_ERROR);
        }
        if (success) {
            String text = layoutJsonService.getLayout();
            FullHttpResponse response = HttpServices.createJsonResponse(text, OK);
            createSession(response, admin);
            return response;
        } else {
            String text = "{\"incorrectPassword\":true}";
            return HttpServices.createJsonResponse(text, OK);
        }
    }

    boolean hasReadAccess(HttpRequest request) {
        if (configService.getUserInterfaceConfig().anonymousAccess() != AnonymousAccess.NONE) {
            return true;
        }
        String sessionId = getSessionId(request);
        if (sessionId == null) {
            return false;
        }
        if (isValidNonExpired(sessionId, true)) {
            return true;
        }
        if (isValidNonExpired(sessionId, false)) {
            return true;
        }
        return false;
    }

    boolean hasAdminAccess(HttpRequest request) {
        if (configService.getUserInterfaceConfig().anonymousAccess() == AnonymousAccess.ADMIN) {
            // anonymous is ok
            return true;
        }
        // anonymous is not ok
        String sessionId = getSessionId(request);
        if (sessionId == null) {
            return false;
        }
        return isValidNonExpired(sessionId, true);
    }

    @Nullable
    String getAuthenticatedUser(HttpRequest request) {
        String sessionId = getSessionId(request);
        if (sessionId == null) {
            return null;
        }
        if (isValidNonExpired(sessionId, true)) {
            return "admin";
        }
        if (isValidNonExpired(sessionId, false)) {
            return "read-only";
        }
        return null;
    }

    FullHttpResponse signOut(HttpRequest request) {
        String sessionId = getSessionId(request);
        if (sessionId != null) {
            adminSessionExpirations.remove(sessionId);
            readOnlySessionExpirations.remove(sessionId);
        }
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
        deleteSessionCookie(response);
        return response;
    }

    void createSession(HttpResponse response, boolean admin) {
        String sessionId = new BigInteger(130, secureRandom).toString(32);
        updateSessionExpiration(sessionId, admin);
        Cookie cookie = new DefaultCookie("GLOWROOT_SESSION_ID", sessionId);
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        response.headers().add(SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
        purgeExpiredSessions();
    }

    void deleteSessionCookie(HttpResponse response) {
        Cookie cookie = new DefaultCookie("GLOWROOT_SESSION_ID", "");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(0);
        cookie.setPath("/");
        response.headers().add(SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
    }

    void clearAllSessions() {
        adminSessionExpirations.clear();
        readOnlySessionExpirations.clear();
    }

    @Nullable
    String getSessionId(HttpRequest request) {
        String cookieHeader = request.headers().get(COOKIE);
        if (cookieHeader == null) {
            return null;
        }
        Set cookies = ServerCookieDecoder.STRICT.decode(cookieHeader);
        for (Cookie cookie : cookies) {
            if (cookie.name().equals("GLOWROOT_SESSION_ID")) {
                return cookie.value();
            }
        }
        return null;
    }

    private void purgeExpiredSessions() {
        long currentTimeMillis = clock.currentTimeMillis();
        purgeExpiredSessions(currentTimeMillis, adminSessionExpirations);
        purgeExpiredSessions(currentTimeMillis, readOnlySessionExpirations);
    }

    private boolean isValidNonExpired(String sessionId, boolean admin) {
        Map sessionExpirations =
                admin ? adminSessionExpirations : readOnlySessionExpirations;
        Long expires = sessionExpirations.get(sessionId);
        if (expires == null || clock.currentTimeMillis() > expires) {
            return false;
        }
        // session is valid and not expired, update expiration
        updateSessionExpiration(sessionId, admin);
        return true;
    }

    private void updateSessionExpiration(String sessionId, boolean admin) {
        Map sessionExpirations =
                admin ? adminSessionExpirations : readOnlySessionExpirations;
        int timeoutMinutes = configService.getUserInterfaceConfig().sessionTimeoutMinutes();
        if (timeoutMinutes == 0) {
            sessionExpirations.put(sessionId, Long.MAX_VALUE);
        } else {
            sessionExpirations.put(sessionId,
                    clock.currentTimeMillis() + MINUTES.toMillis(timeoutMinutes));
        }
    }

    private static void purgeExpiredSessions(long currentTimeMillis,
            Map sessionExpirations) {
        Iterator> i = sessionExpirations.entrySet().iterator();
        while (i.hasNext()) {
            if (i.next().getValue() < currentTimeMillis) {
                i.remove();
            }
        }
    }

    private static boolean validatePassword(String password, String passwordHash)
            throws GeneralSecurityException {
        if (passwordHash.isEmpty()) {
            // need special case for empty password
            return password.isEmpty();
        } else {
            return PasswordHash.validatePassword(password, passwordHash);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy