Authenticate /p/ HTTP and SSH access by password

Use HTTP digest authentication to verify user access to any of
the /p/ URLs which do not permit anonymous requests.

The SSH daemon now also honors the user's password.

Change-Id: I6f8775077b3ee8fcb66a2d07c225f668afa0d530
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2010-01-15 17:55:43 -08:00
parent 37930f8d55
commit 024d69b2fe
12 changed files with 557 additions and 44 deletions

View File

@@ -0,0 +1,102 @@
// Copyright (C) 2010 The Android Open Source Project
//
// 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 com.google.gerrit.sshd;
import static com.google.gerrit.sshd.SshUtil.AUTH_ATTEMPTED_AS;
import static com.google.gerrit.sshd.SshUtil.AUTH_ERROR;
import static com.google.gerrit.sshd.SshUtil.CURRENT_ACCOUNT;
import com.google.gerrit.reviewdb.AccountExternalId;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.sshd.SshScopes.Context;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.mina.core.future.IoFuture;
import org.apache.mina.core.future.IoFutureListener;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.session.ServerSession;
/**
* Authenticates by password through {@link AccountExternalId} entities.
*/
@Singleton
class DatabasePasswordAuth implements PasswordAuthenticator {
private final AccountCache accountCache;
private final SshLog log;
@Inject
DatabasePasswordAuth(final AccountCache ac, final SshLog l) {
accountCache = ac;
log = l;
}
@Override
public boolean authenticate(final String username, final String password,
final ServerSession session) {
AccountState state = accountCache.getByUsername(username);
if (state == null) {
return fail(username, session, "user-not-found");
}
final String p = state.getPassword(username);
if (p == null) {
return fail(username, session, "no-password");
}
if (!p.equals(password)) {
return fail(username, session, "incorrect-password");
}
if (session.setAttribute(CURRENT_ACCOUNT, state.getAccount().getId()) == null) {
// If this is the first time we've authenticated this
// session, record a login event in the log and add
// a close listener to record a logout event.
//
final Context ctx = new Context(session);
final Context old = SshScopes.current.get();
try {
SshScopes.current.set(ctx);
log.onLogin();
} finally {
SshScopes.current.set(old);
}
session.getIoSession().getCloseFuture().addListener(
new IoFutureListener<IoFuture>() {
@Override
public void operationComplete(IoFuture future) {
final Context old = SshScopes.current.get();
try {
SshScopes.current.set(ctx);
log.onLogout();
} finally {
SshScopes.current.set(old);
}
}
});
}
return true;
}
private static boolean fail(final String username,
final ServerSession session, final String err) {
session.setAttribute(AUTH_ATTEMPTED_AS, username);
session.setAttribute(AUTH_ERROR, err);
return false;
}
}

View File

@@ -32,10 +32,6 @@ import java.security.PublicKey;
/**
* Authenticates by public key through {@link AccountSshKey} entities.
* <p>
* The username supplied by the client must be the user's preferred email
* address, as listed in their Account entity. Only keys listed under that
* account as authorized keys are permitted to access the account.
*/
@Singleton
class DatabasePubKeyAuth implements PublickeyAuthenticator {

View File

@@ -60,8 +60,10 @@ import org.apache.sshd.common.util.SecurityUtils;
import org.apache.sshd.server.Command;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.ForwardingFilter;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.UserAuth;
import org.apache.sshd.server.auth.UserAuthPassword;
import org.apache.sshd.server.auth.UserAuthPublicKey;
import org.apache.sshd.server.channel.ChannelDirectTcpip;
import org.apache.sshd.server.channel.ChannelSession;
@@ -119,6 +121,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
@Inject
SshDaemon(final CommandFactory commandFactory,
final PasswordAuthenticator passAuth,
final PublickeyAuthenticator userAuth,
final KeyPairProvider hostKeyProvider, final IdGenerator idGenerator,
@GerritServerConfig final Config cfg, final SshLog sshLog) {
@@ -140,7 +143,7 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
initForwardingFilter();
initSubsystems();
initCompression();
initUserAuth(userAuth);
initUserAuth(passAuth, userAuth);
setKeyPairProvider(hostKeyProvider);
setCommandFactory(commandFactory);
setShellFactory(new NoShell());
@@ -452,9 +455,11 @@ public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
}
@SuppressWarnings("unchecked")
private void initUserAuth(final PublickeyAuthenticator pubkey) {
setUserAuthFactories(Arrays
.<NamedFactory<UserAuth>> asList(new UserAuthPublicKey.Factory()));
private void initUserAuth(final PasswordAuthenticator pass,
final PublickeyAuthenticator pubkey) {
setUserAuthFactories(Arrays.<NamedFactory<UserAuth>> asList(
new UserAuthPublicKey.Factory(), new UserAuthPassword.Factory()));
setPasswordAuthenticator(pass);
setPublickeyAuthenticator(pubkey);
}

View File

@@ -47,6 +47,7 @@ import com.google.inject.servlet.SessionScoped;
import org.apache.sshd.common.KeyPairProvider;
import org.apache.sshd.common.session.AbstractSession;
import org.apache.sshd.server.CommandFactory;
import org.apache.sshd.server.PasswordAuthenticator;
import org.apache.sshd.server.PublickeyAuthenticator;
import org.apache.sshd.server.session.ServerSession;
import org.kohsuke.args4j.spi.OptionHandler;
@@ -81,6 +82,7 @@ public class SshModule extends FactoryModule {
.toProvider(CommandExecutorProvider.class).in(SINGLETON);
bind(PublickeyAuthenticator.class).to(DatabasePubKeyAuth.class);
bind(PasswordAuthenticator.class).to(DatabasePasswordAuth.class);
bind(KeyPairProvider.class).toProvider(HostKeyProvider.class).in(SINGLETON);
install(new DefaultCommandModule());