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

org.restheart.security.authorizers.OriginVetoer Maven / Gradle / Ivy

/*-
 * ========================LICENSE_START=================================
 * restheart-security
 * %%
 * Copyright (C) 2018 - 2024 SoftInstigate
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 * =========================LICENSE_END==================================
 */
package org.restheart.security.authorizers;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.restheart.configuration.ConfigurationException;
import org.restheart.exchange.Request;
import org.restheart.plugins.Inject;
import org.restheart.plugins.OnInit;
import org.restheart.plugins.RegisterPlugin;
import org.restheart.plugins.security.Authorizer;
import org.restheart.plugins.security.Authorizer.TYPE;
import static org.restheart.utils.URLUtils.removeTrailingSlashes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.undertow.util.PathTemplate;
import io.undertow.util.PathTemplateMatcher;

@RegisterPlugin(
    name = "originVetoer",
    description = "protects from CSRF attacks by forbidding requests whose Origin header is not whitelisted",
    enabledByDefault = false,
    authorizerType = TYPE.VETOER)
public class OriginVetoer implements Authorizer {
    private static final Logger LOGGER = LoggerFactory.getLogger(OriginVetoer.class);

    private List whitelist = null;
    private PathTemplateMatcher ignoreLists = new PathTemplateMatcher<>();

    @Inject("config")
    private Map config;

    @OnInit
    public void init() {
        try {
            List _whitelist = arg(config, "whitelist");
            this.whitelist = _whitelist.stream()
                .filter(item -> item != null)
                .map(item -> item.strip())
                .map(item -> item.toLowerCase())
                .map(item -> removeTrailingSlashes(item))
                .map(item -> item.concat("/"))
                .collect(Collectors.toList());

            LOGGER.info("whitelist defined for originVetoer, requests will be accepted with Origin header in {}", this.whitelist);
        } catch(ConfigurationException ce) {
            this.whitelist = null;
            LOGGER.info("No whitelist defined for originVetoer, all Origin headers are accepted");
        }

        try {
            List _ingoreList = arg(config, "ignore-paths");
            _ingoreList.stream()
                .filter(item -> item != null)
                .map(item -> item.strip())
                .map(item -> item.toLowerCase())
                .map(item -> PathTemplate.create(item))
                .forEach(item -> this.ignoreLists.add(item, true));

            LOGGER.info("ignore list defined for originVetoer, requests will be accepted without checking the Origin header for paths in {}", _ingoreList);
        } catch(ConfigurationException ce) {
            this.ignoreLists = null;
            LOGGER.info("No ignoreLists defined for originVetoer, all paths are checked");
        }
    }

    @Override
    public boolean isAllowed(Request request) {
        if (ignoreLists != null && ignoreLists.match(request.getPath()) != null) {
            LOGGER.debug("originVetoer: request is accepted since path is in ignore list");
            return true;
        }

        if (this.whitelist == null || this.whitelist.isEmpty()) {
            return true;
        } else {
            var origin = request.getHeader("Origin");

            if (origin == null) {
                LOGGER.warn("request forbidden by originVetoer due to missing Origin header, whitelist is {}", whitelist);
                return false;
            } else {
                var allowed = this.whitelist.stream().anyMatch(wl -> removeTrailingSlashes(origin.toLowerCase()).concat("/").startsWith(wl));

                if (!allowed) {
                    LOGGER.warn("request forbidden by originVetoer due to Origin header {} not in whitelist {}", origin, whitelist);
                }

                return allowed;
            }
        }
    }

    @Override
    @SuppressWarnings("rawtypes")
    public boolean isAuthenticationRequired(Request request) {
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy