Require branch deletion permission for pushes over HTTP
Since smart HTTP can perform a branch deletion over HTTP requests, we need to disambiguate web requests from the web UI from HTTP requests coming from a git client tool such as git push. Moving all git commands into the AccessPath.GIT category and making a different category for the web UI allows us to tell these apart, so we can correctly require delete branch permission when removing a branch through a git command. This is a safety feature to prevent project owners from accidentally creating or deleting branches over git push, even though they can do this through the web UI without additional access controls. Bug: issue 393 Change-Id: I14cc68e31f5263913f5d9715a8f2241b5766bf23 Signed-off-by: Shawn O. Pearce <sop@google.com> Reviewed-by: Nico Sallembien <nsallembien@google.com>
This commit is contained in:
parent
f6a13f4eed
commit
53b0e7ffb5
@ -0,0 +1,55 @@
|
||||
// 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.httpd;
|
||||
|
||||
import com.google.gerrit.server.AccessPath;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
|
||||
/** Set the WebSession to {@link AccessPath#GIT}. */
|
||||
@Singleton
|
||||
class ProjectAccessPathFilter implements Filter {
|
||||
private final Provider<WebSession> session;
|
||||
|
||||
@Inject
|
||||
ProjectAccessPathFilter(final Provider<WebSession> session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response,
|
||||
FilterChain chain) throws IOException, ServletException {
|
||||
session.get().setAccessPath(AccessPath.GIT);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
@ -50,6 +50,7 @@ class UrlModule extends ServletModule {
|
||||
serve("/ssh_info").with(SshInfoServlet.class);
|
||||
serve("/static/*").with(StaticServlet.class);
|
||||
|
||||
filter("/p/*").through(ProjectAccessPathFilter.class);
|
||||
filter("/p/*").through(ProjectDigestFilter.class);
|
||||
serve("/p/*").with(ProjectServlet.class);
|
||||
|
||||
|
@ -64,6 +64,7 @@ public final class WebSession {
|
||||
private final WebSessionManager manager;
|
||||
private final AnonymousUser anonymous;
|
||||
private final IdentifiedUser.RequestFactory identified;
|
||||
private AccessPath accessPath = AccessPath.WEB_UI;
|
||||
private Cookie outCookie;
|
||||
|
||||
private Key key;
|
||||
@ -132,7 +133,7 @@ public final class WebSession {
|
||||
|
||||
CurrentUser getCurrentUser() {
|
||||
if (isSignedIn()) {
|
||||
return identified.create(AccessPath.WEB, val.getAccountId());
|
||||
return identified.create(accessPath, val.getAccountId());
|
||||
}
|
||||
return anonymous;
|
||||
}
|
||||
@ -148,6 +149,11 @@ public final class WebSession {
|
||||
saveCookie();
|
||||
}
|
||||
|
||||
/** Change the access path from the default of {@link AccessPath#WEB_UI}. */
|
||||
void setAccessPath(AccessPath path) {
|
||||
accessPath = path;
|
||||
}
|
||||
|
||||
/** Set the user account for this current request only. */
|
||||
void setUserAccountId(Account.Id id) {
|
||||
key = new Key("id:" + id);
|
||||
|
@ -19,11 +19,14 @@ public enum AccessPath {
|
||||
/** An unknown access path, probably should not be special. */
|
||||
UNKNOWN,
|
||||
|
||||
/** Access through the web interface. */
|
||||
WEB,
|
||||
/** Access through the web UI. */
|
||||
WEB_UI,
|
||||
|
||||
/** Access through an SSH command, e.g. git fetch or push. */
|
||||
SSH,
|
||||
/** Access through an SSH command that is not invoked by Git. */
|
||||
SSH_COMMAND,
|
||||
|
||||
/** Access from a Git client using any Git protocol. */
|
||||
GIT,
|
||||
|
||||
/** Access through replication */
|
||||
REPLICATION;
|
||||
|
@ -39,7 +39,7 @@ public class PeerDaemonUser extends CurrentUser {
|
||||
|
||||
@Inject
|
||||
protected PeerDaemonUser(AuthConfig authConfig, @Assisted SocketAddress peer) {
|
||||
super(AccessPath.SSH, authConfig);
|
||||
super(AccessPath.SSH_COMMAND, authConfig);
|
||||
|
||||
final HashSet<AccountGroup.Id> g = new HashSet<AccountGroup.Id>();
|
||||
g.add(authConfig.getAdministratorsGroup());
|
||||
|
@ -121,7 +121,7 @@ public class RefControl {
|
||||
public boolean canCreate(RevWalk rw, RevObject object) {
|
||||
boolean owner;
|
||||
switch (getCurrentUser().getAccessPath()) {
|
||||
case WEB:
|
||||
case WEB_UI:
|
||||
owner = isOwner();
|
||||
break;
|
||||
|
||||
@ -179,10 +179,10 @@ public class RefControl {
|
||||
*/
|
||||
public boolean canDelete() {
|
||||
switch (getCurrentUser().getAccessPath()) {
|
||||
case WEB:
|
||||
case WEB_UI:
|
||||
return isOwner() || canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
|
||||
|
||||
case SSH:
|
||||
case GIT:
|
||||
return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
|
||||
|
||||
default:
|
||||
|
@ -105,7 +105,8 @@ class DatabasePasswordAuth implements PasswordAuthenticator {
|
||||
|
||||
private IdentifiedUser createUser(final SshSession sd,
|
||||
final AccountState state) {
|
||||
return userFactory.create(AccessPath.SSH, new Provider<SocketAddress>() {
|
||||
return userFactory.create(AccessPath.SSH_COMMAND,
|
||||
new Provider<SocketAddress>() {
|
||||
@Override
|
||||
public SocketAddress get() {
|
||||
return sd.getRemoteAddress();
|
||||
|
@ -184,7 +184,8 @@ class DatabasePubKeyAuth implements PublickeyAuthenticator {
|
||||
|
||||
private IdentifiedUser createUser(final SshSession sd,
|
||||
final SshKeyCacheEntry key) {
|
||||
return userFactory.create(AccessPath.SSH, new Provider<SocketAddress>() {
|
||||
return userFactory.create(AccessPath.SSH_COMMAND,
|
||||
new Provider<SocketAddress>() {
|
||||
@Override
|
||||
public SocketAddress get() {
|
||||
return sd.getRemoteAddress();
|
||||
|
@ -114,7 +114,7 @@ public final class SuExec extends BaseCommand {
|
||||
}
|
||||
|
||||
return new SshSession(session.get(), peer, userFactory.create(
|
||||
AccessPath.SSH, new Provider<SocketAddress>() {
|
||||
AccessPath.SSH_COMMAND, new Provider<SocketAddress>() {
|
||||
@Override
|
||||
public SocketAddress get() {
|
||||
return peer;
|
||||
|
Loading…
Reference in New Issue
Block a user