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

org.apache.sshd.common.keyprovider.AbstractResourceKeyPairProvider Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you 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.apache.sshd.common.keyprovider;

import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;

import org.apache.sshd.common.NamedResource;
import org.apache.sshd.common.config.keys.FilePasswordProvider;
import org.apache.sshd.common.session.SessionContext;
import org.apache.sshd.common.util.GenericUtils;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.io.resource.IoResource;
import org.apache.sshd.common.util.io.resource.ResourceStreamProvider;
import org.apache.sshd.common.util.security.SecurityUtils;

/**
 * @param   Type of resource from which the {@link KeyPair} is generated
 * @author     Apache MINA SSHD Project
 */
public abstract class AbstractResourceKeyPairProvider extends AbstractKeyPairProvider {
    private FilePasswordProvider passwordFinder;
    /*
     * NOTE: the map is case insensitive even for Linux, as it is (very) bad practice to have 2 key files that differ
     * from one another only in their case...
     */
    private final Map> cacheMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

    protected AbstractResourceKeyPairProvider() {
        super();
    }

    public FilePasswordProvider getPasswordFinder() {
        return passwordFinder;
    }

    public void setPasswordFinder(FilePasswordProvider passwordFinder) {
        this.passwordFinder = passwordFinder;
    }

    /**
     * Checks which of the new resources we already loaded and can keep the associated key pair
     *
     * @param resources The collection of new resources - can be {@code null}/empty in which case the cache is cleared
     */
    protected void resetCacheMap(Collection resources) {
        // if have any cached pairs then see what we can keep from previous load
        Collection toDelete = Collections.emptySet();
        synchronized (cacheMap) {
            if (cacheMap.size() <= 0) {
                return; // already empty - nothing to keep
            }

            if (GenericUtils.isEmpty(resources)) {
                cacheMap.clear();
                return;
            }

            for (Object r : resources) {
                String resourceKey = ValidateUtils.checkNotNullAndNotEmpty(Objects.toString(r, null), "No resource key value");
                if (cacheMap.containsKey(resourceKey)) {
                    continue;
                }

                if (toDelete.isEmpty()) {
                    toDelete = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
                }

                if (!toDelete.add(resourceKey)) {
                    continue; // debug breakpoint
                }
            }

            if (GenericUtils.size(toDelete) > 0) {
                toDelete.forEach(cacheMap::remove);
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("resetCacheMap({}) removed previous cached keys for {}", resources, toDelete);
        }
    }

    protected Iterable loadKeys(SessionContext session, Collection resources) {
        if (GenericUtils.isEmpty(resources)) {
            return Collections.emptyList();
        } else {
            return () -> new KeyPairIterator(session, resources);
        }
    }

    protected IoResource getIoResource(SessionContext session, R resource) {
        return IoResource.forResource(resource);
    }

    protected Iterable doLoadKeys(SessionContext session, R resource)
            throws IOException, GeneralSecurityException {
        IoResource ioResource
                = ValidateUtils.checkNotNull(getIoResource(session, resource), "No I/O resource available for %s", resource);
        String resourceKey
                = ValidateUtils.checkNotNullAndNotEmpty(ioResource.getName(), "No resource string value for %s", resource);
        Iterable ids;
        synchronized (cacheMap) {
            // check if lucky enough to have already loaded this file
            ids = cacheMap.get(resourceKey);
        }

        if (ids != null) {
            if (log.isTraceEnabled()) {
                log.trace("doLoadKeys({}) using cached identifiers", resourceKey);
            }
            return ids;
        }

        ids = doLoadKeys(session, ioResource, resource, getPasswordFinder());
        if (ids != null) {
            boolean reusedKey;
            synchronized (cacheMap) {
                // if somebody else beat us to it, use the cached key - just in case file contents changed
                reusedKey = cacheMap.containsKey(resourceKey);
                if (reusedKey) {
                    ids = cacheMap.get(resourceKey);
                } else {
                    cacheMap.put(resourceKey, ids);
                }
            }

            if (log.isDebugEnabled()) {
                log.debug("doLoadKeys({}) {}", resourceKey, reusedKey ? "re-loaded" : "loaded");
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("doLoadKeys({}) no key loaded", resourceKey);
            }
        }

        return ids;
    }

    protected Iterable doLoadKeys(
            SessionContext session, NamedResource resourceKey, R resource, FilePasswordProvider provider)
            throws IOException, GeneralSecurityException {
        try (InputStream inputStream = openKeyPairResource(session, resourceKey, resource)) {
            return doLoadKeys(session, resourceKey, inputStream, provider);
        }
    }

    protected InputStream openKeyPairResource(
            SessionContext session, NamedResource resourceKey, R resource)
            throws IOException {
        if (resourceKey instanceof ResourceStreamProvider) {
            return ((ResourceStreamProvider) resourceKey).openInputStream();
        }

        throw new StreamCorruptedException("Cannot open resource data for " + resource);
    }

    protected Iterable doLoadKeys(
            SessionContext session, NamedResource resourceKey, InputStream inputStream, FilePasswordProvider provider)
            throws IOException, GeneralSecurityException {
        return SecurityUtils.loadKeyPairIdentities(session, resourceKey, inputStream, provider);
    }

    protected class KeyPairIterator implements Iterator {
        protected final SessionContext session;
        private final Iterator iterator;
        private Iterator currentIdentities;
        private KeyPair nextKeyPair;
        private boolean nextKeyPairSet;

        protected KeyPairIterator(SessionContext session, Collection resources) {
            this.session = session;
            this.iterator = resources.iterator();
        }

        @Override
        public boolean hasNext() {
            return nextKeyPairSet || setNextObject();
        }

        @Override
        public KeyPair next() {
            if (!nextKeyPairSet && !setNextObject()) {
                throw new NoSuchElementException("Out of files to try");
            }
            nextKeyPairSet = false;
            return nextKeyPair;
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException("loadKeys(files) Iterator#remove() N/A");
        }

        @SuppressWarnings("synthetic-access")
        private boolean setNextObject() {
            nextKeyPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
            if (nextKeyPair != null) {
                nextKeyPairSet = true;
                return true;
            }

            while (iterator.hasNext()) {
                R r = iterator.next();
                try {
                    Iterable ids = doLoadKeys(session, r);
                    currentIdentities = (ids == null) ? null : ids.iterator();
                    nextKeyPair = KeyIdentityProvider.exhaustCurrentIdentities(currentIdentities);
                } catch (Throwable e) {
                    warn("Failed ({}) to load key resource={}: {}", e.getClass().getSimpleName(), r, e.getMessage(), e);
                    nextKeyPair = null;
                    continue;
                }

                if (nextKeyPair != null) {
                    nextKeyPairSet = true;
                    return true;
                }
            }

            return false;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy