Package com.goodow.moon.web.server.auth

Source Code of com.goodow.moon.web.server.auth.OAuthCallbackHandler

/*
* Copyright 2012 Goodow.com.
*
* 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.goodow.moon.web.server.auth;

import com.google.api.client.http.HttpResponseException;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService.SetPolicy;
import com.google.common.net.UriEscapers;
import com.google.gxp.base.GxpContext;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.walkaround.util.server.RetryHelper.PermanentFailure;
import com.google.walkaround.util.server.appengine.MemcacheTable;
import com.google.walkaround.util.server.servlet.BadRequestException;
import com.google.walkaround.wave.server.Flag;
import com.google.walkaround.wave.server.FlagName;
import com.google.walkaround.wave.server.auth.AccountStore;
import com.google.walkaround.wave.server.auth.AccountStore.Record;
import com.google.walkaround.wave.server.auth.OAuthCredentials;
import com.google.walkaround.wave.server.auth.StableUserId;
import com.google.walkaround.wave.server.auth.XsrfHelper;
import com.google.walkaround.wave.server.gxp.AuthPopup;

import org.json.JSONException;
import org.json.JSONObject;
import org.waveprotocol.wave.model.wave.ParticipantId;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class OAuthCallbackHandler extends
    com.google.walkaround.wave.server.auth.OAuthCallbackHandler {

  private static final Logger log = Logger.getLogger(OAuthCallbackHandler.class.getName());

  @Inject AccountStore accountStore;
  @Inject UserContext userContext;
  @Inject @Flag(FlagName.ANALYTICS_ACCOUNT) String analyticsAccount;
  @Inject OAuthProvider.Helper oAuthProviderHelp;
  @Inject Map<String, OAuthProvider> oAuthProviders;
  @Inject Provider<XsrfHelper> xsrfHelper;
  @Inject @Flag(FlagName.XSRF_TOKEN_EXPIRY_SECONDS) int expirySeconds;
  private final MemcacheTable<String, StableUserId> authorizedCodes;

  @Inject
  OAuthCallbackHandler(MemcacheTable.Factory memcacheFactory) {
    authorizedCodes = memcacheFactory.create("OAuth");
  }

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    String errorCode = req.getParameter("error");
    if (errorCode != null) {
      String errorDescription = req.getParameter("error_description");
      log.info("error: " + errorCode + ", description: " + errorDescription);
      String errorMessage;
      if ("access_denied".equals(errorCode)) {
        errorMessage = "请点击上面任一按钮, 在新页面登录, 然后允许访问.";
      } else {
        errorMessage = "An error occured (" + errorCode + "): " + errorDescription;
      }
      log.info("errorMessage=" + errorMessage);
      writeRegularError(req, resp, errorMessage);
      return;
    }

    String code = requireParameter(req, "code");
    String state = requireParameter(req, "state");
    log.info("code=" + code + ", state=" + state);
    String[] split = state.split(" ");
    if (split.length != 2) {
      throw new BadRequestException("state格式错误: " + state);
    }
    OAuthCredentials credentials;
    try {
      credentials = oAuthProviderHelp.exchangeCodeForToken(split[0], code);
    } catch (IOException e) {
      log.log(Level.WARNING, "Failed attempt, trying again", e);
      if (e instanceof HttpResponseException) {
        HttpResponseException f = (HttpResponseException) e;
        ByteArrayOutputStream o = new ByteArrayOutputStream();
        f.getResponse().getRequest().getContent().writeTo(o);
        // TODO(ohler): Use correct character set.
        log.warning("content of rejected request: " + o.toString());
        log.warning("rejection response body: " + f.getResponse().parseAsString());
      }
      resp.sendRedirect(req.getRequestURI() + "?code=" + urlEncode(code) + "&state="
          + urlEncode(state) + "&tryagain=true");
      return;
    }

    // TODO(ohler): Disable user switching in OAuth dialog once Google's OAuth
    // API supports that. (We don't need to be too defensive about account
    // mismatches since there's no real harm in allowing a user to use another
    // Google account for contact information and to import waves from; but it
    // is confusing, so we should disable it.)
    userContext.setOAuthCredentials(credentials);
    userContext.setOAuthProvider(oAuthProviders.get(split[0]));

    Record userInfo = userContext.getOAuthProvider().getUserInfo();
    userContext.setUserId(userInfo.getUserId());
    userContext.setParticipantId(userInfo.getParticipantId());
    log.info("User context: " + userContext);
    writeAccountRecordFromContext();
    authorizedCodes.put(split[1], userInfo.getUserId(), Expiration.byDeltaSeconds(30),
        SetPolicy.ADD_ONLY_IF_NOT_PRESENT);

    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    Cookie uid = new Cookie(TokenBasedAccountLookup.USER_ID_KEY, userContext.getUserId().getId());
    uid.setMaxAge(expirySeconds);
    resp.addCookie(uid);
    Cookie secret =
        new Cookie(TokenBasedAccountLookup.SECRET_TOKEN_COOKIE_KEY, xsrfHelper.get().createToken(
            userContext.getOAuthCredentials().getAccessToken()));
    secret.setMaxAge(expirySeconds);
    resp.addCookie(secret);
    AuthPopup.write(resp.getWriter(), new GxpContext(getLocale(req)), analyticsAccount, null);
  }

  @Override
  public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException,
      ServletException {
    String code = requireParameter(req, "code");
    String clientId = requireParameter(req, "client_id");
    String clientSecret = requireParameter(req, "client_secret");
    AccountStore.Record record = verify(clientId, clientSecret, code);
    JSONObject toRtn = new JSONObject();
    if (record != null) {
      try {
        StableUserId userId = record.getUserId();
        ParticipantId participantId = record.getParticipantId();
        OAuthCredentials oAuthCredentials = record.getOAuthCredentials();
        userContext.setUserId(userId);
        userContext.setParticipantId(participantId);
        userContext.setOAuthCredentials(oAuthCredentials);

        toRtn.put(TokenBasedAccountLookup.USER_ID_KEY, userId.getId());
        toRtn.put("participantId", participantId.getAddress());
        toRtn.put("access_token", xsrfHelper.get().createToken(oAuthCredentials.getAccessToken()));
      } catch (JSONException e) {
        throw new RuntimeException("Bad JSON: " + toRtn, e);
      }
    }
    resp.setStatus(200);
    resp.setContentType("application/json");
    resp.setCharacterEncoding("UTF-8");
    resp.getWriter().print(toRtn.toString());
  }

  private String urlEncode(String s) {
    return UriEscapers.uriQueryStringEscaper(false).escape(s);
  }

  private AccountStore.Record verify(String clientId, String clientSecret, String code)
      throws IOException {
    if (!oAuthProviders.get("google").getClientId().equals(clientId)
        || !oAuthProviders.get("google").getClientSecret().equals(clientSecret)) {
      return null;
    }
    StableUserId userId = authorizedCodes.get(code);
    if (userId == null) {
      return null;
    }
    try {
      return accountStore.get(userId);
    } catch (PermanentFailure e) {
      throw new IOException("Failed to read account record", e);
    } finally {
      authorizedCodes.delete(code);
    }
  }

  private void writeAccountRecordFromContext() throws IOException {
    try {
      accountStore.put(new AccountStore.Record(userContext.getUserId(), userContext
          .getParticipantId(), userContext.getOAuthCredentials()));
    } catch (PermanentFailure e) {
      throw new IOException("Failed to write account record", e);
    }
  }

  private void writeRegularError(HttpServletRequest req, HttpServletResponse resp,
      String errorMessage) throws IOException {
    resp.setContentType("text/html");
    resp.setCharacterEncoding("UTF-8");
    AuthPopup.write(resp.getWriter(), new GxpContext(getLocale(req)), analyticsAccount,
        errorMessage);
  }

}
TOP

Related Classes of com.goodow.moon.web.server.auth.OAuthCallbackHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.