import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.codec.binary.Base64;
import org.apache.sshd.common.util.Buffer;
* @author James Moger
*
*/
-public class FileKeyManager implements IKeyManager {
+public class FileKeyManager extends IKeyManager {
protected final IRuntimeManager runtimeManager;
+ protected final Map<File, Long> lastModifieds;
+
public FileKeyManager(IRuntimeManager runtimeManager) {
this.runtimeManager = runtimeManager;
+ this.lastModifieds = new ConcurrentHashMap<File, Long>();
}
@Override
}
@Override
- public List<PublicKey> getKeys(String username) {
+ protected boolean isStale(String username) {
+ File keystore = getKeystore(username);
+ if (!keystore.exists()) {
+ // keystore may have been deleted
+ return true;
+ }
+
+ if (lastModifieds.containsKey(keystore)) {
+ // compare modification times
+ long lastModified = lastModifieds.get(keystore);
+ return lastModified != keystore.lastModified();
+ }
+
+ // assume stale
+ return true;
+ }
+
+ @Override
+ protected List<PublicKey> getKeysImpl(String username) {
try {
- File keys = getKeystore(username);
- if (!keys.exists()) {
+ log.info("loading keystore for {}", username);
+ File keystore = getKeystore(username);
+ if (!keystore.exists()) {
return null;
}
- if (keys.exists()) {
+ if (keystore.exists()) {
List<PublicKey> list = new ArrayList<PublicKey>();
- for (String entry : Files.readLines(keys, Charsets.ISO_8859_1)) {
+ for (String entry : Files.readLines(keystore, Charsets.ISO_8859_1)) {
if (entry.trim().length() == 0) {
// skip blanks
continue;
if (list.isEmpty()) {
return null;
}
+
+ lastModifieds.put(keystore, keystore.lastModified());
return list;
}
} catch (IOException e) {
// write keystore
String content = Joiner.on("\n").join(lines).trim().concat("\n");
Files.write(content, keystore, Charsets.ISO_8859_1);
+
+ lastModifieds.remove(keystore);
+ keyCache.invalidate(username);
return true;
} catch (IOException e) {
throw new RuntimeException("Cannot add ssh key", e);
String content = Joiner.on("\n").join(lines).trim().concat("\n");
Files.write(content, keystore, Charsets.ISO_8859_1);
}
+
+ lastModifieds.remove(keystore);
+ keyCache.invalidate(username);
return true;
}
} catch (IOException e) {
@Override
public boolean removeAllKeys(String username) {
- return getKeystore(username).delete();
+ File keystore = getKeystore(username);
+ if (keystore.delete()) {
+ lastModifieds.remove(keystore);
+ keyCache.invalidate(username);
+ return true;
+ }
+ return false;
}
protected File getKeystore(String username) {
package com.gitblit.transport.ssh;
import java.security.PublicKey;
+import java.text.MessageFormat;
import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
/**
- *
+ *
* @author James Moger
*
*/
-public interface IKeyManager {
-
- IKeyManager start();
-
- boolean isReady();
-
- IKeyManager stop();
-
- List<PublicKey> getKeys(String username);
-
- boolean addKey(String username, String data);
-
- boolean removeKey(String username, String data);
-
- boolean removeAllKeys(String username);
+public abstract class IKeyManager {
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ protected final LoadingCache<String, List<PublicKey>> keyCache = CacheBuilder
+ .newBuilder().
+ expireAfterAccess(15, TimeUnit.MINUTES).
+ maximumSize(100)
+ .build(new CacheLoader<String, List<PublicKey>>() {
+ @Override
+ public List<PublicKey> load(String username) {
+ return getKeysImpl(username);
+ }
+ });
+
+ public abstract IKeyManager start();
+
+ public abstract boolean isReady();
+
+ public abstract IKeyManager stop();
+
+ public final List<PublicKey> getKeys(String username) {
+ try {
+ if (isStale(username)) {
+ keyCache.invalidate(username);
+ }
+ return keyCache.get(username);
+ } catch (ExecutionException e) {
+ log.error(MessageFormat.format("failed to retrieve keys for {0}", username), e);
+ }
+ return null;
+ }
+
+ protected abstract boolean isStale(String username);
+
+ protected abstract List<PublicKey> getKeysImpl(String username);
+
+ public abstract boolean addKey(String username, String data);
+
+ public abstract boolean removeKey(String username, String data);
+
+ public abstract boolean removeAllKeys(String username);
}
/**
* Rejects all SSH key management requests.
- *
+ *
* @author James Moger
*
*/
-public class NullKeyManager implements IKeyManager {
+public class NullKeyManager extends IKeyManager {
public NullKeyManager() {
}
-
+
@Override
public String toString() {
return getClass().getSimpleName();
public NullKeyManager start() {
return this;
}
-
+
@Override
public boolean isReady() {
return true;
}
-
+
@Override
public NullKeyManager stop() {
return this;
}
@Override
- public List<PublicKey> getKeys(String username) {
+ protected boolean isStale(String username) {
+ return false;
+ }
+
+ @Override
+ protected List<PublicKey> getKeysImpl(String username) {
return null;
}
public boolean addKey(String username, String data) {
return false;
}
-
+
@Override
public boolean removeKey(String username, String data) {
return false;
import java.security.PublicKey;
import java.util.List;
import java.util.Locale;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import com.gitblit.manager.IAuthenticationManager;
import com.gitblit.models.UserModel;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
/**
*
protected final IAuthenticationManager authManager;
- LoadingCache<String, List<PublicKey>> sshKeyCache = CacheBuilder
- .newBuilder().
- expireAfterAccess(15, TimeUnit.MINUTES).
- maximumSize(100)
- .build(new CacheLoader<String, List<PublicKey>>() {
- @Override
- public List<PublicKey> load(String username) {
- return keyManager.getKeys(username);
- }
- });
-
public SshKeyAuthenticator(IKeyManager keyManager, IAuthenticationManager authManager) {
this.keyManager = keyManager;
this.authManager = authManager;
}
username = username.toLowerCase(Locale.US);
- try {
- List<PublicKey> keys = sshKeyCache.get(username);
- if (keys == null || keys.isEmpty()) {
- log.info("{} has not added any public keys for ssh authentication", username);
- return false;
- }
+ List<PublicKey> keys = keyManager.getKeys(username);
+ if (keys == null || keys.isEmpty()) {
+ log.info("{} has not added any public keys for ssh authentication", username);
+ return false;
+ }
- for (PublicKey key : keys) {
- if (key.equals(suppliedKey)) {
- UserModel user = authManager.authenticate(username, key);
- if (user != null) {
- client.setUser(user);
- return true;
- }
+ for (PublicKey key : keys) {
+ if (key.equals(suppliedKey)) {
+ UserModel user = authManager.authenticate(username, key);
+ if (user != null) {
+ client.setUser(user);
+ return true;
}
}
- } catch (ExecutionException e) {
}
log.warn("could not authenticate {} for SSH using the supplied public key", username);
public IKeyManager getKeyManager() {
return keyManager;
}
-
- public Cache<String, List<PublicKey>> getKeyCache() {
- return sshKeyCache;
- }
}
keyManager.addKey(username, key);
log.info("added SSH public key for {}", username);
}
- authenticator.getKeyCache().invalidate(username);
}
}
log.info("removed SSH public key from {}", username);
}
}
- authenticator.getKeyCache().invalidate(username);
}
}
if (!deleteSshKeys.isEmpty()) {
deleteSshKeys(deleteSshKeys);
}
- authenticator.getKeyCache().invalidate(user);
}
private void addSshKeys(List<String> sshKeys) throws UnloggedFailure,