// Copyright (C) 2008 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.rpc; import com.google.gerrit.common.auth.SignInRequired; import com.google.gerrit.common.errors.NotSignedInException; import com.google.gerrit.httpd.WebSession; import com.google.gson.GsonBuilder; import com.google.gwtjsonrpc.client.RemoteJsonService; import com.google.gwtjsonrpc.server.ActiveCall; import com.google.gwtjsonrpc.server.JsonServlet; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Base JSON servlet to ensure the current user is not forged. */ @SuppressWarnings("serial") final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall> { private final Provider<WebSession> session; private final RemoteJsonService service; @Inject GerritJsonServlet(final Provider<WebSession> w, final RemoteJsonService s) { session = w; service = s; } @Override protected GerritCall createActiveCall(final HttpServletRequest req, final HttpServletResponse rsp) { return new GerritCall(session.get(), req, rsp); } @Override protected GsonBuilder createGsonBuilder() { final GsonBuilder g = super.createGsonBuilder(); g.registerTypeAdapter(org.eclipse.jgit.diff.Edit.class, new org.eclipse.jgit.diff.EditDeserializer()); return g; } @Override protected void preInvoke(final GerritCall call) { super.preInvoke(call); if (call.isComplete()) { return; } if (call.getMethod().getAnnotation(SignInRequired.class) != null) { // If SignInRequired is set on this method we must have both a // valid XSRF token *and* have the user signed in. Doing these // checks also validates that they agree on the user identity. // if (!call.requireXsrfValid() || !session.get().isSignedIn()) { call.onFailure(new NotSignedInException()); return; } } } @Override protected Object createServiceHandle() { return service; } static class GerritCall extends ActiveCall { private final WebSession session; GerritCall(final WebSession session, final HttpServletRequest i, final HttpServletResponse o) { super(i, o); this.session = session; } @Override public void onFailure(final Throwable error) { if (error instanceof IllegalArgumentException || error instanceof IllegalStateException) { super.onFailure(error); } else if (error instanceof OrmException || error instanceof RuntimeException) { onInternalFailure(error); } else { super.onFailure(error); } } @Override public boolean xsrfValidate() { final String keyIn = getXsrfKeyIn(); if (keyIn == null || "".equals(keyIn)) { // Anonymous requests don't need XSRF protection, they shouldn't // be able to cause critical state changes. // return !session.isSignedIn(); } else { // The session must exist, and must be using this token. // return session.isSignedIn() && session.isTokenValid(keyIn); } } } }