http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/agent/passwd/PasswordRobot.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/agent/passwd/PasswordRobot.java b/src/org/waveprotocol/box/server/robots/agent/passwd/PasswordRobot.java deleted file mode 100644 index a2a0daf..0000000 --- a/src/org/waveprotocol/box/server/robots/agent/passwd/PasswordRobot.java +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.agent.passwd; - -import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.CANNOT_CHANGE_PASSWORD_FOR_USER; -import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.changeUserPassword; - -import com.google.common.collect.ImmutableMap; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Singleton; - -import org.apache.commons.cli.CommandLine; -import org.eclipse.jetty.util.MultiMap; -import org.waveprotocol.box.server.authentication.HttpRequestBasedCallbackHandler; -import org.waveprotocol.box.server.persistence.PersistenceException; -import org.waveprotocol.box.server.robots.agent.AbstractCliRobotAgent; -import org.waveprotocol.wave.model.wave.InvalidParticipantAddress; -import org.waveprotocol.wave.model.wave.ParticipantId; - -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.security.auth.Subject; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.login.Configuration; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; - -/** - * Robot agent that allows a user to change her own password. The userId should - * be from this wave domain. - * - * When the robot is added to a wave, it prints basic description and then - * inspects text entered by the user. When a new line char is entered it scans - * the last line of the text in the blip and parses it using Apache CLI command - * line interpreter. If the command found to be valid, the robot validates user - * credentials and then changes the password of the user to a new one. - * - * @author [email protected] (Yuri Zelikov) - */ -@SuppressWarnings("serial") -@Singleton -public final class PasswordRobot extends AbstractCliRobotAgent { - - private static final Logger LOG = Logger.getLogger(PasswordRobot.class.getName()); - public static final String ROBOT_URI = AGENT_PREFIX_URI + "/passwd/user"; - - /** Configuration for the LoginContext. */ - private final Configuration configuration; - - @Inject - public PasswordRobot(Injector injector) { - super(injector); - configuration = injector.getInstance(Configuration.class); - } - - @Override - protected String maybeExecuteCommand(CommandLine commandLine, String modifiedBy) { - String robotMessage = null; - // Get the user that wants to change her own password. - if (!modifiedBy.endsWith("@" + getWaveDomain())) { - // Can change passwords only for users on this wave domain. - robotMessage = - String.format("User %s does not belong to the @%s domain\n", modifiedBy, - getWaveDomain()); - } else { - String[] args = commandLine.getArgs(); - try { - ParticipantId participantId = ParticipantId.of(modifiedBy); - if (args.length == 2) { - // If current password is empty, i.e. "", then user should pass - // only the new password. - args = Arrays.copyOf(args, 3); - args[2] = args[1]; - args[1] = ""; - } - String oldPassword = args[1]; - String newPassword = args[2]; - verifyCredentials(oldPassword, participantId); - changeUserPassword(newPassword, participantId, getAccountStore()); - robotMessage = - String.format("Changed password for user %s, the new password is: %s", modifiedBy, - newPassword); - LOG.info(modifiedBy + " changed password for user: " + modifiedBy); - } catch (IllegalArgumentException e) { - robotMessage = e.getMessage(); - LOG.log(Level.SEVERE, "userId: " + modifiedBy, e); - } catch (PersistenceException e) { - robotMessage = CANNOT_CHANGE_PASSWORD_FOR_USER + modifiedBy; - LOG.log(Level.SEVERE, "userId: " + modifiedBy, e); - } catch (InvalidParticipantAddress e) { - robotMessage = CANNOT_CHANGE_PASSWORD_FOR_USER + modifiedBy; - LOG.log(Level.SEVERE, "userId: " + modifiedBy, e); - } catch (LoginException e) { - robotMessage = - CANNOT_CHANGE_PASSWORD_FOR_USER + modifiedBy - + ". Please verify your old password"; - LOG.log(Level.SEVERE, "userId: " + modifiedBy, e); - } - } - return robotMessage; - } - - /** - * Verifies user credentials. - * - * @param oldPassword the password to verify. - * @param participantId the participantId of the user. - * @throws LoginException if the user provided incorrect password. - */ - private void verifyCredentials(String password, ParticipantId participantId) - throws LoginException { - MultiMap<String> parameters = new MultiMap<String>(); - parameters.putAllValues(ImmutableMap.of("password", password, "address", participantId.getAddress())); - CallbackHandler callbackHandler = new HttpRequestBasedCallbackHandler(parameters); - LoginContext context = new LoginContext("Wave", new Subject(), callbackHandler, configuration); - // If authentication fails, login() will throw a LoginException. - context.login(); - } - - @Override - public String getFullDescription() { - return getShortDescription() + " If your password is empty - enter only the new password.\n" - + getUsage() + "\nExample: " + getCommandName() + " " + getExample(); - } - - @Override - public String getCmdLineSyntax() { - return "[OPTIONS] [OLD_PASSWORD] [NEW_PASSWORD]"; - } - - @Override - public String getExample() { - return "old_password new_password"; - } - - @Override - public String getShortDescription() { - return "The command allows users to change their own password. " - + "Please make sure to use it in a wave without other participants. " - + "It is also advised to remove yourself from the wave " - + "when you finished changing the password."; - } - - @Override - public String getCommandName() { - return "passwd"; - } - - @Override - public String getRobotName() { - return "Passwd-Bot"; - } - - @Override - public int getMinNumOfArguments() { - return 1; - } - - @Override - public int getMaxNumOfArguments() { - return 2; - } - - @Override - public String getRobotUri() { - return ROBOT_URI; - } - - @Override - public String getRobotId() { - return "passwd-bot"; - } -}
http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/agent/registration/RegistrationRobot.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/agent/registration/RegistrationRobot.java b/src/org/waveprotocol/box/server/robots/agent/registration/RegistrationRobot.java deleted file mode 100644 index cbf3332..0000000 --- a/src/org/waveprotocol/box/server/robots/agent/registration/RegistrationRobot.java +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.agent.registration; - -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Singleton; -import com.typesafe.config.Config; -import org.apache.commons.cli.CommandLine; -import org.waveprotocol.box.server.persistence.AccountStore; -import org.waveprotocol.box.server.persistence.PersistenceException; -import org.waveprotocol.box.server.robots.agent.AbstractCliRobotAgent; -import org.waveprotocol.wave.model.wave.InvalidParticipantAddress; -import org.waveprotocol.wave.model.wave.ParticipantId; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.CANNOT_CREATE_USER; -import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.createUser; - -/** - * Robot agent that handles the user registration by an admin - * - * @author [email protected] (Ali Lown) - */ -@SuppressWarnings("serial") -@Singleton -public final class RegistrationRobot extends AbstractCliRobotAgent { - - private static final Logger LOG = Logger.getLogger(RegistrationRobot.class.getName()); - public static final String ROBOT_URI = AGENT_PREFIX_URI + "/registration/admin"; - - /** The address of the admin user as defined in the server configuration. */ - private final String serverAdminId; - - /** Account store with user and robot accounts. */ - private final AccountStore accountStore; - - @Inject - public RegistrationRobot(Injector injector) { - super(injector); - serverAdminId = - injector.getInstance(Config.class).getString("administration.admin_user"); - accountStore = injector.getInstance(AccountStore.class); - } - - @Override - protected String maybeExecuteCommand(CommandLine commandLine, String modifiedBy) { - String robotMessage; - // Verify that the user that attempts to create a user has admin privileges. - if (!modifiedBy.equals(serverAdminId)) { - robotMessage = - "User " + modifiedBy + " is not authorized to use " + getCommandName() + " command."; - } else { - String userId = null; - try { - String[] args = commandLine.getArgs(); - userId = args[1]; - String password = args[2]; - // Add domain to the user id if needed. - userId = userId + (userId.contains("@") ? "" : "@" + getWaveDomain()); - ParticipantId participantId = ParticipantId.of(userId); - createUser(accountStore, participantId, password); - robotMessage = String.format("Created user %s, the password is: %s\n", userId, password); - LOG.log(Level.INFO, "Created user " + userId + " by " + modifiedBy); - } catch (IllegalArgumentException e) { - LOG.log(Level.SEVERE, userId, e); - robotMessage = e.getMessage(); - } catch (PersistenceException | InvalidParticipantAddress e) { - robotMessage = CANNOT_CREATE_USER + userId; - LOG.log(Level.SEVERE, "userId: " + userId, e); - } - } - - return robotMessage; - } - - @Override - public int getMinNumOfArguments() { - return 2; - } - - @Override - public int getMaxNumOfArguments() { - return 2; - } - - @Override - public String getCommandName() { - return "register"; - } - - @Override - public String getFullDescription() { - return getShortDescription() + "\n" + getUsage() + "\nExample: " + getCommandName() + " " - + getExample(); - } - - @Override - public String getCmdLineSyntax() { - return "[OPTIONS] [USERNAME] [PASSWORD]"; - } - - @Override - public String getExample() { - return "user_id password"; - } - - @Override - public String getShortDescription() { - return "The command allows the admin to register other users. " - + "Please make sure to use it in a wave without other participants. " - + "It is also advised to remove yourself from the wave " - + "when you finished creating users."; - } - - @Override - public String getRobotName() { - return "Registration-Bot"; - } - - @Override - public String getRobotUri() { - return ROBOT_URI; - } - - @Override - public String getRobotId() { - return "registration-bot"; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/agent/welcome/WelcomeRobot.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/agent/welcome/WelcomeRobot.java b/src/org/waveprotocol/box/server/robots/agent/welcome/WelcomeRobot.java deleted file mode 100755 index fddab5a..0000000 --- a/src/org/waveprotocol/box/server/robots/agent/welcome/WelcomeRobot.java +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.agent.welcome; - -import com.google.common.base.Preconditions; -import com.google.common.collect.Sets; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Singleton; -import com.google.wave.api.Wavelet; -import com.typesafe.config.Config; -import org.waveprotocol.box.server.account.RobotAccountData; -import org.waveprotocol.box.server.persistence.PersistenceException; -import org.waveprotocol.box.server.robots.agent.AbstractBaseRobotAgent; -import org.waveprotocol.box.server.robots.util.RobotsUtil; -import org.waveprotocol.wave.model.id.InvalidIdException; -import org.waveprotocol.wave.model.id.WaveId; -import org.waveprotocol.wave.model.id.WaveletId; -import org.waveprotocol.wave.model.wave.ParticipantId; - -import java.io.IOException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static org.waveprotocol.box.server.robots.agent.RobotAgentUtil.appendLine; - - -/** - * The robot that adds a "Welcome" wave to the inbox of new users. - * - * @author [email protected] (Yuri Zelikov) - */ -@SuppressWarnings("serial") -@Singleton -public class WelcomeRobot extends AbstractBaseRobotAgent { - - - private static final Logger LOG = Logger.getLogger(WelcomeRobot.class.getName()); - public static final String ROBOT_URI = AGENT_PREFIX_URI + "/welcome"; - - /** The id of the wave that serves as a template for the welcome wave. */ - private WaveId welcomeWaveId = null; - - @Inject - public WelcomeRobot(Injector injector) { - super(injector); - String welcomeWaveIdStr = - injector.getInstance(Config.class).getString("administration.welcome_wave_id"); - if (!"".equals(welcomeWaveIdStr)) { - try { - welcomeWaveId = WaveId.ofChecked(getWaveDomain(), welcomeWaveIdStr); - } catch (InvalidIdException e) { - LOG.log(Level.WARNING, "Problem parsing welcome wave id: " + welcomeWaveIdStr); - } - } - } - - /** - * Greets new users by creating a new wave with welcome message and - * adding it to the inbox of the new user. - * - * @param id the participant id of the new user. - * @throws IOException if there is a problem submitting the new wave. - */ - public void greet(ParticipantId id) throws IOException { - Preconditions.checkNotNull(id); - RobotAccountData account = null; - String rpcUrl = getFrontEndAddress() + "/robot/rpc"; - try { - account = - getAccountStore() - .getAccount(ParticipantId.ofUnsafe(getRobotId() + "@" + getWaveDomain())).asRobot(); - } catch (PersistenceException e) { - LOG.log(Level.WARNING, "Cannot fetch account data for robot id: " + getRobotId(), e); - } - if (account != null) { - setupOAuth(account.getId().getAddress(), account.getConsumerSecret(), rpcUrl); - Wavelet newWelcomeWavelet = newWave(getWaveDomain(), Sets.newHashSet(id.getAddress())); - if (welcomeWaveId != null) { - Wavelet templateWelcomeWavelet = - fetchWavelet(welcomeWaveId, WaveletId.of(getWaveDomain(), "conv+root"), rpcUrl); - RobotsUtil.copyBlipContents(templateWelcomeWavelet.getRootBlip(), - newWelcomeWavelet.getRootBlip()); - } else { - appendLine(newWelcomeWavelet.getRootBlip(), "Welcome to " + getWaveDomain() + "!"); - } - submit(newWelcomeWavelet, rpcUrl); - } - } - - @Override - public String getRobotUri() { - return ROBOT_URI; - } - - @Override - public String getRobotId() { - return "welcome-bot"; - } - - @Override - protected String getRobotName() { - return "Welcome-Bot"; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/dataapi/BaseApiServlet.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/dataapi/BaseApiServlet.java b/src/org/waveprotocol/box/server/robots/dataapi/BaseApiServlet.java deleted file mode 100644 index 6ddf7b4..0000000 --- a/src/org/waveprotocol/box/server/robots/dataapi/BaseApiServlet.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.dataapi; - -import com.google.common.collect.Lists; -import com.google.wave.api.InvalidRequestException; -import com.google.wave.api.JsonRpcResponse; -import com.google.wave.api.OperationRequest; -import com.google.wave.api.ProtocolVersion; -import com.google.wave.api.RobotSerializer; -import com.google.wave.api.data.converter.EventDataConverterManager; -import com.google.wave.api.impl.GsonFactory; - -import net.oauth.OAuthAccessor; -import net.oauth.OAuthException; -import net.oauth.OAuthMessage; -import net.oauth.OAuthValidator; -import org.waveprotocol.box.server.robots.OperationContext; -import org.waveprotocol.box.server.robots.OperationContextImpl; -import org.waveprotocol.box.server.robots.OperationResults; -import org.waveprotocol.box.server.robots.OperationServiceRegistry; -import org.waveprotocol.box.server.robots.util.ConversationUtil; -import org.waveprotocol.box.server.robots.util.LoggingRequestListener; -import org.waveprotocol.box.server.robots.util.OperationUtil; -import org.waveprotocol.box.server.waveserver.WaveletProvider; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.util.logging.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URISyntaxException; -import java.util.LinkedList; -import java.util.List; - -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * The base {@link HttpServlet} for {@link DataApiServlet} and - * {@link ActiveApiServlet}. - * - * @author [email protected] (Lennard de Rijk) - * @author [email protected] (Yuri Z.) - */ -@SuppressWarnings("serial") -public abstract class BaseApiServlet extends HttpServlet { - - private static final Log LOG = Log.get(BaseApiServlet.class); - private static final WaveletProvider.SubmitRequestListener LOGGING_REQUEST_LISTENER = - new LoggingRequestListener(LOG); - private static final String JSON_CONTENT_TYPE = "application/json"; - - private final RobotSerializer robotSerializer; - private final EventDataConverterManager converterManager; - private final WaveletProvider waveletProvider; - private final OperationServiceRegistry operationRegistry; - private final ConversationUtil conversationUtil; - private final OAuthValidator validator; - - /** Holds incoming operation requests. */ - private List<OperationRequest> operations; - - public BaseApiServlet(RobotSerializer robotSerializer, - EventDataConverterManager converterManager, WaveletProvider waveletProvider, - OperationServiceRegistry operationRegistry, ConversationUtil conversationUtil, - OAuthValidator validator) { - this.robotSerializer = robotSerializer; - this.converterManager = converterManager; - this.waveletProvider = waveletProvider; - this.conversationUtil = conversationUtil; - this.operationRegistry = operationRegistry; - this.validator = validator; - } - - /** - * Validates OAUTH and executes operations. - * - * @param req the request. - * @param resp the response. - * @param message the OAUTH message. - * @param accessor the OAUTH accessor. - * @param participant the author for which to perform the robot operations. - * @throws IOException if encountered errors during writing of a response. - */ - protected final void processOpsRequest(HttpServletRequest req, HttpServletResponse resp, OAuthMessage message, - OAuthAccessor accessor, ParticipantId participant) throws IOException { - try { - validator.validateMessage(message, accessor); - } catch (OAuthException e) { - LOG.info("The message does not conform to OAuth", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } catch (URISyntaxException e) { - LOG.info("The message URL is invalid", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - - String apiRequest; - try { - // message.readBodyAsString() doesn't work due to a NPE in the OAuth - // libraries. - BufferedReader reader = req.getReader(); - apiRequest = reader.readLine(); - } catch (IOException e) { - LOG.warning("Unable to read the incoming request", e); - throw e; - } - - LOG.info("Received the following Json: " + apiRequest); - try { - operations = robotSerializer.deserializeOperations(apiRequest); - } catch (InvalidRequestException e) { - LOG.info("Unable to parse Json to list of OperationRequests: " + apiRequest); - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, - "Unable to parse Json to list of OperationRequests: " + apiRequest); - return; - } - - // Create an unbound context. - ProtocolVersion version = OperationUtil.getProtocolVersion(operations); - OperationContextImpl context = new OperationContextImpl( - waveletProvider, converterManager.getEventDataConverter(version), conversationUtil); - - executeOperations(context, operations, participant); - handleResults(context, resp, version); - } - - /** - * Executes operations in the given context. - * - * @param context the context to perform the operations in. - * @param operations the operations to perform. - * @param author the author for which to perform the robot operations. - */ - private void executeOperations( - OperationContext context, List<OperationRequest> operations, ParticipantId author) { - for (OperationRequest operation : operations) { - OperationUtil.executeOperation(operation, operationRegistry, context, author); - } - } - - /** - * Handles an {@link OperationResults} by submitting the deltas that are - * generated and writing a response to the robot. - * - * @param results the results of the operations performed. - * @param resp the servlet to write the response in. - * @param version the version of the protocol to use for writing a response. - * @throws IOException if the response can not be written. - */ - private void handleResults( - OperationResults results, HttpServletResponse resp, ProtocolVersion version) - throws IOException { - OperationUtil.submitDeltas(results, waveletProvider, LOGGING_REQUEST_LISTENER); - - // Ensure that responses are returned in the same order as corresponding - // requests. - LinkedList<JsonRpcResponse> responses = Lists.newLinkedList(); - for (OperationRequest operation : operations) { - String opId = operation.getId(); - JsonRpcResponse response = results.getResponses().get(opId); - responses.addLast(response); - } - - String jsonResponse = - robotSerializer.serialize(responses, GsonFactory.JSON_RPC_RESPONSE_LIST_TYPE, version); - LOG.info("Returning the following Json: " + jsonResponse); - - // Write the response back through the HttpServlet - try { - resp.setContentType(JSON_CONTENT_TYPE); - PrintWriter writer = resp.getWriter(); - writer.append(jsonResponse); - writer.flush(); - resp.setStatus(HttpServletResponse.SC_OK); - } catch (IOException e) { - LOG.severe("IOException during writing of a response", e); - throw e; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/dataapi/DataApiOAuthServlet.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/dataapi/DataApiOAuthServlet.java b/src/org/waveprotocol/box/server/robots/dataapi/DataApiOAuthServlet.java deleted file mode 100644 index 6c31853..0000000 --- a/src/org/waveprotocol/box/server/robots/dataapi/DataApiOAuthServlet.java +++ /dev/null @@ -1,416 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.dataapi; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.base.Strings; -import com.google.common.cache.CacheBuilder; -import com.google.gxp.base.GxpContext; -import com.google.inject.Inject; -import com.google.inject.name.Named; - -import net.oauth.OAuth; -import net.oauth.OAuthAccessor; -import net.oauth.OAuthConsumer; -import net.oauth.OAuthException; -import net.oauth.OAuthMessage; -import net.oauth.OAuthProblemException; -import net.oauth.OAuthServiceProvider; -import net.oauth.OAuthValidator; -import net.oauth.server.HttpRequestMessage; - -import org.waveprotocol.box.server.authentication.SessionManager; -import org.waveprotocol.box.server.gxp.OAuthAuthorizeTokenPage; -import org.waveprotocol.wave.model.id.TokenGenerator; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.util.logging.Log; -import org.waveprotocol.box.server.gxp.OAuthAuthorizationCodePage; -import org.waveprotocol.wave.model.util.CharBase64; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -import javax.inject.Singleton; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * Servlet responsible for the 3-legged OAuth dance required for the Data api. - * - * @author [email protected] (Lennard de Rijk) - * @author [email protected] (A. Kaplanov) - */ -@SuppressWarnings("serial") -@Singleton -public class DataApiOAuthServlet extends HttpServlet { - - public static final String DATA_API_OAUTH_PATH = "/robot/dataapi/oauth"; - private static final Log LOG = Log.get(DataApiOAuthServlet.class); - private static final String ANONYMOUS_TOKEN = "anonymous"; - private static final String ANONYMOUS_TOKEN_SECRET = "anonymous"; - private static final String HTML_CONTENT_TYPE = "text/html"; - private static final String PLAIN_CONTENT_TYPE = "text/plain"; - private static final int TOKEN_LENGTH = 8; - private static final int XSRF_TOKEN_TIMEOUT_HOURS = 12; - - private final String requestTokenPath; - private final String authorizeTokenPath; - private final String accessTokenPath; - private final String allTokensPath; - private final OAuthServiceProvider serviceProvider; - private final OAuthValidator validator; - private final DataApiTokenContainer tokenContainer; - private final SessionManager sessionManager; - private final TokenGenerator tokenGenerator; - // TODO(ljvderijk): We should refactor this and use it for our other pages. - private final ConcurrentMap<ParticipantId, String> xsrfTokens; - - @Inject - public DataApiOAuthServlet(@Named("request_token_path") String requestTokenPath, - @Named("authorize_token_path") String authorizeTokenPath, - @Named("access_token_path") String accessTokenPath, - @Named("all_tokens_path") String allTokensPath, - OAuthServiceProvider serviceProvider, - OAuthValidator validator, DataApiTokenContainer tokenContainer, - SessionManager sessionManager, TokenGenerator tokenGenerator) { - this.requestTokenPath = requestTokenPath; - this.authorizeTokenPath = authorizeTokenPath; - this.accessTokenPath = accessTokenPath; - this.allTokensPath = allTokensPath; - this.serviceProvider = serviceProvider; - this.validator = validator; - this.tokenContainer = tokenContainer; - this.sessionManager = sessionManager; - this.tokenGenerator = tokenGenerator; - this.xsrfTokens = - CacheBuilder.newBuilder() - .expireAfterWrite(XSRF_TOKEN_TIMEOUT_HOURS, TimeUnit.HOURS) - .<ParticipantId, String>build().asMap(); - } - - @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { - routeRequest(req, resp); - } - - - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { - routeRequest(req, resp); - } - - /** Routes all requests to the appropriate handler */ - private void routeRequest(HttpServletRequest req, HttpServletResponse resp) throws IOException { - String pathInfo = req.getPathInfo(); - if (pathInfo.equals(requestTokenPath)) { - doRequestToken(req, resp); - } else if (pathInfo.equals(authorizeTokenPath)) { - doAuthorizeToken(req, resp); - } else if (pathInfo.equals(accessTokenPath)) { - doExchangeToken(req, resp); - } else if (pathInfo.equals(allTokensPath)) { - doAllTokens(req, resp); - } else { - resp.setStatus(HttpServletResponse.SC_NOT_FOUND); - } - } - - /** - * Handles the request to get a new unauthorized request token. - */ - private void doRequestToken(HttpServletRequest req, HttpServletResponse resp) throws IOException { - OAuthMessage message = new HttpRequestMessage(req, req.getRequestURL().toString()); - - // Anyone can generate a request token. - OAuthConsumer consumer = - new OAuthConsumer("", ANONYMOUS_TOKEN, ANONYMOUS_TOKEN_SECRET, serviceProvider); - OAuthAccessor accessor = new OAuthAccessor(consumer); - try { - validator.validateMessage(message, accessor); - } catch (OAuthException e) { - LOG.info("The message does not conform to OAuth", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } catch (URISyntaxException e) { - LOG.info("The message URL is invalid", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - - accessor = tokenContainer.generateRequestToken(consumer); - - resp.setContentType(OAuth.FORM_ENCODED); - ServletOutputStream out = resp.getOutputStream(); - OAuth.formEncode(OAuth.newList( - OAuth.OAUTH_TOKEN, accessor.requestToken, - OAuth.OAUTH_TOKEN_SECRET, accessor.tokenSecret, - OAuth.OAUTH_CALLBACK_CONFIRMED, "true"), - out); - out.close(); - resp.setStatus(HttpServletResponse.SC_OK); - } - - /** - * Handles the request to authorize a token. Checks if the user is logged in, - * if not the user is redirected to the login page. - * - * <p> - * If it is a GET request the user will be asked whether permission should be - * given. For a POST request we will handle the user's decision. - */ - private void doAuthorizeToken(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - // Check if the OAuth parameters are present, even if we don't use them - // during a GET request. - OAuthMessage message = new HttpRequestMessage(req, req.getRequestURL().toString()); - try { - message.requireParameters(OAuth.OAUTH_CALLBACK, OAuth.OAUTH_TOKEN); - } catch (OAuthProblemException e) { - LOG.info("Parameter absent", e); - resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); - return; - } - - // Check if the user is logged in, else redirect to login. - ParticipantId user = sessionManager.getLoggedInUser(req.getSession(false)); - if (user == null) { - resp.sendRedirect(sessionManager.getLoginUrl( - DATA_API_OAUTH_PATH + authorizeTokenPath + "?" + req.getQueryString())); - return; - } - - // Check if the request token is valid, note that this doesn't hold after - // the call to the container since the token might time out. - try { - tokenContainer.getRequestTokenAccessor(message.getToken()); - } catch (OAuthProblemException e) { - LOG.info("Trying to load a non existing token for authorization", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - - if (req.getMethod().equals("GET")) { - doAuthorizeTokenGet(req, resp, user); - } else if (req.getMethod().equals("POST")) { - doAuthorizeTokenPost(req, resp, user, message); - } else { - throw new IllegalStateException( - "This method shouldn't be called outside GET or POST requests"); - } - } - - /** - * Handles the GET request to authorize a token by displaying the page asking - * for user's permission. - * - * @param req {@link HttpServletRequest} received. - * @param resp {@link HttpServletResponse} to write the response in. - * @param user User who wants to authorize the token. - */ - private void doAuthorizeTokenGet( - HttpServletRequest req, HttpServletResponse resp, ParticipantId user) throws IOException { - Preconditions.checkNotNull(user, "User must be supplied"); - // Ask the user for permission. - OAuthAuthorizeTokenPage.write(resp.getWriter(), new GxpContext(req.getLocale()), - user.getAddress(), getOrGenerateXsrfToken(user)); - resp.setContentType(HTML_CONTENT_TYPE); - resp.setStatus(HttpServletResponse.SC_OK); - return; - } - - /** - * Method that handles the POST request for the authorize token page. The - * token will be rejected if the user pressed the cancel button. Otherwise the - * token will be authorized. - * - * @param req {@link HttpServletRequest} received. - * @param resp {@link HttpServletResponse} to write the response in. - * @param user user who is authorizing the token. - * @param message the {@link OAuthMessage} present in the request. - */ - private void doAuthorizeTokenPost( - HttpServletRequest req, HttpServletResponse resp, ParticipantId user, OAuthMessage message) - throws IOException { - Preconditions.checkNotNull(user, "User must be supplied"); - - // Check the XSRF token. - if (Strings.isNullOrEmpty(req.getParameter("token")) - || !req.getParameter("token").equals(xsrfTokens.get(user))) { - LOG.warning( - "Request without a valid xsrf token received from " + req.getRemoteAddr() + " for user " - + user); - resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid XSRF token"); - return; - } - // Check whether the user agreed to give access. - if (req.getParameter("cancel") != null) { - try { - tokenContainer.rejectRequestToken(message.getToken()); - } catch (OAuthProblemException e) { - LOG.info("Rejecting a request token failed", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - resp.setContentType(PLAIN_CONTENT_TYPE); - resp.getWriter().append("No access granted, you can now close this page."); - resp.setStatus(HttpServletResponse.SC_OK); - return; - } else if (req.getParameter("agree") == null) { - // User did not agree nor disagree, bad request. - LOG.warning( - "Bad request when authorzing a token from " + req.getRemoteAddr() + " for user " + user); - resp.setStatus(HttpServletResponse.SC_BAD_REQUEST); - return; - } - - // Authorize the token. - OAuthAccessor accessor; - try { - accessor = tokenContainer.authorizeRequestToken(message.getToken(), user); - } catch (OAuthProblemException e) { - LOG.info("Authorizing a request token failed", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - - // Create the callback url and send the user to it - String callback = message.getParameter(OAuth.OAUTH_CALLBACK); - callback = OAuth.addParameters(callback, OAuth.OAUTH_TOKEN, accessor.requestToken); - resp.sendRedirect(callback); - } - - /** - * Exchanges an authorized request token with an access token. - */ - private void doExchangeToken(HttpServletRequest req, HttpServletResponse resp) - throws IOException { - OAuthMessage message = new HttpRequestMessage(req, req.getRequestURL().toString()); - - String requestToken = message.getToken(); - OAuthAccessor accessor; - try { - accessor = tokenContainer.getRequestTokenAccessor(requestToken); - } catch (OAuthProblemException e) { - LOG.info("Request token unknown", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - - try { - validator.validateMessage(message, accessor); - } catch (OAuthException e) { - LOG.info("The message does not conform to OAuth", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } catch (URISyntaxException e) { - LOG.info("The message URL is invalid", e); - resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - return; - } - - OAuthAccessor authorizedAccessor; - try { - authorizedAccessor = tokenContainer.generateAccessToken(accessor.requestToken); - } catch (OAuthProblemException e) { - LOG.info("Request token unknown", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - - resp.setContentType(OAuth.FORM_ENCODED); - ServletOutputStream out = resp.getOutputStream(); - OAuth.formEncode(OAuth.newList( - OAuth.OAUTH_TOKEN, authorizedAccessor.accessToken, - OAuth.OAUTH_TOKEN_SECRET, authorizedAccessor.tokenSecret, - OAuth.OAUTH_CALLBACK_CONFIRMED, "true"), - out); - out.close(); - resp.setStatus(HttpServletResponse.SC_OK); - } - - /** - * Perform full Auth dance and print tokens. - */ - private void doAllTokens(HttpServletRequest req, HttpServletResponse resp) throws IOException { - OAuthMessage message = new HttpRequestMessage(req, req.getRequestURL().toString()); - String requestToken = message.getToken(); - if (requestToken == null) { - OAuthConsumer consumer = - new OAuthConsumer("", ANONYMOUS_TOKEN, ANONYMOUS_TOKEN_SECRET, serviceProvider); - OAuthAccessor accessor = tokenContainer.generateRequestToken(consumer); - String url = accessor.consumer.serviceProvider.userAuthorizationURL - + "?oauth_token=" + accessor.requestToken + "&oauth_callback=" - + req.getRequestURL().toString() + "&hd=default"; - resp.sendRedirect(url); - } else { - OAuthAccessor accessor; - try { - accessor = tokenContainer.getRequestTokenAccessor(requestToken); - } catch (OAuthProblemException e) { - LOG.info("Request token unknown", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - OAuthAccessor authorizedAccessor; - try { - authorizedAccessor = tokenContainer.generateAccessToken(accessor.requestToken); - } catch (OAuthProblemException e) { - LOG.info("Request token unknown", e); - resp.sendError(e.getHttpStatusCode(), e.getMessage()); - return; - } - String authorizationCode = authorizedAccessor.requestToken + " " - + authorizedAccessor.accessToken + " " + authorizedAccessor.tokenSecret; - String base64AuthCode = CharBase64.encode(authorizationCode.getBytes()); - OAuthAuthorizationCodePage.write(resp.getWriter(), new GxpContext(req.getLocale()), - base64AuthCode); - resp.setContentType(HTML_CONTENT_TYPE); - resp.setStatus(HttpServletResponse.SC_OK); - } - } - - /** - * Gets or generates an XSRF token for the given user. - * - * <p> - * XSRF is an attack where, for instance, another web site makes the user's - * browser do a POST request to authorize a request token. Because the user's - * browser might contain an valid login cookie for "Wave in a Box" this would - * succeed. By adding a hidden field on each form with a random token and - * checking the presence of this token the attack can be countered because the - * attacker does not know the token. - * - * @param user the user to generate a token for. - */ - @VisibleForTesting - String getOrGenerateXsrfToken(ParticipantId user) { - String token = tokenGenerator.generateToken(TOKEN_LENGTH); - String previousToken = xsrfTokens.putIfAbsent(user, token); - if (previousToken != null) { - token = previousToken; - } - return token; - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/dataapi/DataApiOperationServiceRegistry.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/dataapi/DataApiOperationServiceRegistry.java b/src/org/waveprotocol/box/server/robots/dataapi/DataApiOperationServiceRegistry.java deleted file mode 100644 index cfaf64a..0000000 --- a/src/org/waveprotocol/box/server/robots/dataapi/DataApiOperationServiceRegistry.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.dataapi; - -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.wave.api.OperationType; - -import org.waveprotocol.box.server.robots.AbstractOperationServiceRegistry; -import org.waveprotocol.box.server.robots.operations.*; - -/** - * A registry of {@link OperationService}s for the data API. - * - * @author [email protected] (Lennard de Rijk) - */ -public final class DataApiOperationServiceRegistry extends AbstractOperationServiceRegistry { - // Suppressing warnings about operations that are deprecated but still used by - // the default client libraries - @SuppressWarnings("deprecation") - @Inject - public DataApiOperationServiceRegistry(Injector injector) { - super(); - - // Register all the OperationProviders - register(OperationType.ROBOT_NOTIFY, DoNothingService.create()); - register(OperationType.ROBOT_NOTIFY_CAPABILITIES_HASH, DoNothingService.create()); - register(OperationType.WAVELET_ADD_PARTICIPANT_NEWSYNTAX, ParticipantServices.create()); - register(OperationType.WAVELET_APPEND_BLIP, BlipOperationServices.create()); - register(OperationType.WAVELET_REMOVE_PARTICIPANT_NEWSYNTAX, ParticipantServices.create()); - register(OperationType.BLIP_CONTINUE_THREAD, BlipOperationServices.create()); - register(OperationType.BLIP_CREATE_CHILD, BlipOperationServices.create()); - register(OperationType.BLIP_DELETE, BlipOperationServices.create()); - register(OperationType.DOCUMENT_APPEND_INLINE_BLIP, BlipOperationServices.create()); - register(OperationType.DOCUMENT_APPEND_MARKUP, BlipOperationServices.create()); - register(OperationType.DOCUMENT_INSERT_INLINE_BLIP, BlipOperationServices.create()); - register( - OperationType.DOCUMENT_INSERT_INLINE_BLIP_AFTER_ELEMENT, BlipOperationServices.create()); - register(OperationType.ROBOT_CREATE_WAVELET, CreateWaveletService.create()); - register(OperationType.ROBOT_FETCH_WAVE, FetchWaveService.create()); - register(OperationType.DOCUMENT_MODIFY, DocumentModifyService.create()); - register(OperationType.ROBOT_SEARCH, injector.getInstance(SearchService.class)); - register(OperationType.WAVELET_SET_TITLE, WaveletSetTitleService.create()); - register(OperationType.ROBOT_FOLDER_ACTION, FolderActionService.create()); - register(OperationType.ROBOT_FETCH_PROFILES, injector.getInstance(FetchProfilesService.class)); - register(OperationType.ROBOT_EXPORT_SNAPSHOT, ExportSnapshotService.create()); - register(OperationType.ROBOT_EXPORT_DELTAS, ExportDeltasService.create()); - register(OperationType.ROBOT_EXPORT_ATTACHMENT, injector.getInstance(ExportAttachmentService.class)); - register(OperationType.ROBOT_IMPORT_DELTAS, injector.getInstance(ImportDeltasService.class)); - register(OperationType.ROBOT_IMPORT_ATTACHMENT, injector.getInstance(ImportAttachmentService.class)); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/dataapi/DataApiServlet.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/dataapi/DataApiServlet.java b/src/org/waveprotocol/box/server/robots/dataapi/DataApiServlet.java deleted file mode 100644 index 18743b5..0000000 --- a/src/org/waveprotocol/box/server/robots/dataapi/DataApiServlet.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.dataapi; - -import com.google.inject.Inject; -import com.google.inject.name.Named; -import com.google.wave.api.RobotSerializer; -import com.google.wave.api.data.converter.EventDataConverterManager; - -import net.oauth.OAuth; -import net.oauth.OAuthAccessor; -import net.oauth.OAuthMessage; -import net.oauth.OAuthProblemException; -import net.oauth.OAuthValidator; -import net.oauth.server.HttpRequestMessage; - -import org.waveprotocol.box.server.robots.OperationServiceRegistry; -import org.waveprotocol.box.server.robots.util.ConversationUtil; -import org.waveprotocol.box.server.waveserver.WaveletProvider; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.util.logging.Log; - -import java.io.IOException; - -import javax.inject.Singleton; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * {@link HttpServlet} that serves as the endpoint for the Data Api. - * - * @author [email protected] (Lennard de Rijk) - */ -@SuppressWarnings("serial") -@Singleton -public class DataApiServlet extends BaseApiServlet { - - private static final Log LOG = Log.get(DataApiServlet.class); - private final DataApiTokenContainer tokenContainer; - - @Inject - public DataApiServlet(RobotSerializer robotSerializer, - EventDataConverterManager converterManager, WaveletProvider waveletProvider, - @Named("DataApiRegistry") OperationServiceRegistry operationRegistry, - ConversationUtil conversationUtil, OAuthValidator validator, - DataApiTokenContainer tokenContainer) { - super(robotSerializer, converterManager, waveletProvider, operationRegistry, conversationUtil, - validator); - this.tokenContainer = tokenContainer; - } - - /** - * Entry point for the Data API Calls. - */ - @Override - protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { - OAuthMessage message = new HttpRequestMessage(req, req.getRequestURL().toString()); - - OAuthAccessor accessor; - try { - message.requireParameters(OAuth.OAUTH_TOKEN); - accessor = tokenContainer.getAccessTokenAccessor(message.getParameter(OAuth.OAUTH_TOKEN)); - } catch (OAuthProblemException e) { - LOG.info("No valid OAuth token present", e); - // Have to set status here manually, cannot use e.getHttpStatusCode - // because message.requireParameters doesn't set it in the exception. - resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); - return; - } - ParticipantId participant = - (ParticipantId) accessor.getProperty(DataApiTokenContainer.USER_PROPERTY_NAME); - - processOpsRequest(req, resp, message, accessor, participant); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/dataapi/DataApiTokenContainer.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/dataapi/DataApiTokenContainer.java b/src/org/waveprotocol/box/server/robots/dataapi/DataApiTokenContainer.java deleted file mode 100644 index 6032088..0000000 --- a/src/org/waveprotocol/box/server/robots/dataapi/DataApiTokenContainer.java +++ /dev/null @@ -1,226 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.dataapi; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Preconditions; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.MapMaker; -import com.google.inject.Inject; -import com.google.inject.Singleton; - -import net.oauth.OAuth; -import net.oauth.OAuthAccessor; -import net.oauth.OAuthConsumer; -import net.oauth.OAuthProblemException; - -import org.waveprotocol.box.server.util.OAuthUtil; -import org.waveprotocol.wave.model.id.TokenGenerator; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.util.logging.Log; - -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.TimeUnit; - -/** - * Container to handle request and access tokens for the Data Api. - * - * @author [email protected] (Lennard de Rijk) - */ -@Singleton -public final class DataApiTokenContainer { - - private static final Log LOG = Log.get(DataApiTokenContainer.class); - - /** Length of a token in number of characters */ - private static final int TOKEN_LENGTH = 48; - - /** Number of minutes a request token is valid */ - private static final int REQUEST_TOKEN_EXPIRATION = 10; - - /** - * Number of minutes an access token is valid, public so this can be shown to - * the user - */ - public static final int ACCESS_TOKEN_EXPIRATION = 60; - public static final String USER_PROPERTY_NAME = "user"; - - /** Map containing unauthorized accessors indexed on their request token */ - private ConcurrentMap<String, OAuthAccessor> requestTokenAccessors; - - /** Map containing the authorized accessors indexed on the access token */ - private ConcurrentMap<String, OAuthAccessor> accessTokenAccessors; - - /** Used to generate OAuth tokens */ - private final TokenGenerator tokenGenerator; - - @Inject - @VisibleForTesting - DataApiTokenContainer(TokenGenerator tokenGenerator) { - this.tokenGenerator = tokenGenerator; - - requestTokenAccessors = - CacheBuilder.newBuilder().expireAfterWrite(REQUEST_TOKEN_EXPIRATION, TimeUnit.MINUTES) - .<String, OAuthAccessor>build().asMap(); - accessTokenAccessors = - CacheBuilder.newBuilder().expireAfterWrite(ACCESS_TOKEN_EXPIRATION, TimeUnit.MINUTES) - .<String, OAuthAccessor>build().asMap(); - } - - /** - * Gets the {@link OAuthAccessor} that is identified by the given request - * token. Any changes made to the accessor's fields, except the consumer, will - * not be reflected in this container. - * - * @param requestToken the request token used for identification. - * @throws OAuthProblemException if the token does not map to an accessor. - */ - public OAuthAccessor getRequestTokenAccessor(String requestToken) throws OAuthProblemException { - OAuthAccessor accessor = requestTokenAccessors.get(requestToken); - if (accessor == null) { - OAuthProblemException exception = - OAuthUtil.newOAuthProblemException(OAuth.Problems.TOKEN_REJECTED); - exception.setParameter(OAuth.OAUTH_TOKEN, requestToken); - throw exception; - } - return accessor.clone(); - } - - /** - * Gets the authorized {@link OAuthAccessor} that is identified by the given - * access token. Any changes made to the accessor's fields, except the - * consumer, will not be reflected in this container. - * - * @param accessToken the access token used for identification. - * @throws OAuthProblemException if the token does not map to an accessor. - */ - public OAuthAccessor getAccessTokenAccessor(String accessToken) throws OAuthProblemException { - OAuthAccessor accessor = accessTokenAccessors.get(accessToken); - if (accessor == null) { - OAuthProblemException exception = - OAuthUtil.newOAuthProblemException(OAuth.Problems.TOKEN_REJECTED); - exception.setParameter(OAuth.OAUTH_TOKEN, accessToken); - throw exception; - } - return accessor.clone(); - } - - /** - * Generates a new request token for the given {@link OAuthConsumer}. - * - * @param consumer the consumer to generate the token for. - */ - public OAuthAccessor generateRequestToken(OAuthConsumer consumer) { - Preconditions.checkNotNull(consumer, "Consumer must not be null"); - - // Accessor can be generated up front with a token secret that does not need - // to be unique. - OAuthAccessor accessor = new OAuthAccessor(consumer); - accessor.tokenSecret = generateToken(); - - do { - accessor.requestToken = generateToken(); - } while (requestTokenAccessors.putIfAbsent(accessor.requestToken, accessor) != null); - - return accessor.clone(); - } - - /** - * Authorizes a request token to be exchanged for an access token. - * - * @param requestToken the request token used for identification. - * @param user the user that has authorized the token. - * @throws OAuthProblemException if the request token does not map to an - * accessor or if the token was already used. - */ - public OAuthAccessor authorizeRequestToken(String requestToken, ParticipantId user) - throws OAuthProblemException { - Preconditions.checkNotNull(user, "User must not be null"); - - OAuthAccessor accessor = getRequestTokenAccessor(requestToken); - - if (accessor.getProperty(USER_PROPERTY_NAME) != null) { - throw OAuthUtil.newOAuthProblemException(OAuth.Problems.TOKEN_USED); - } - - accessor.setProperty(USER_PROPERTY_NAME, user); - requestTokenAccessors.put(requestToken, accessor); - - LOG.info("Authorized request token for " + user); - return accessor.clone(); - } - - /** - * Rejects authorization of a request token. - * - * @param requestToken the request token used for identification. - * @throws OAuthProblemException if the request token does not map to an - * accessor or if the token was already used. - */ - public void rejectRequestToken(String requestToken) throws OAuthProblemException { - OAuthAccessor accessor = getRequestTokenAccessor(requestToken); - - if (accessor.getProperty(USER_PROPERTY_NAME) != null) { - throw OAuthUtil.newOAuthProblemException(OAuth.Problems.TOKEN_USED); - } - - // Can't use remove(String, OAuthAccessor) since equals is not defined. - requestTokenAccessors.remove(requestToken); - LOG.info("Rejected request token " + requestToken); - } - - /** - * Authorize the {@link OAuthAccessor} by generating a new access token and - * token secret. - * - * @param requestToken the requestToken used for identifying the accessor that - * needs to be authorized. - * @return a new {@link OAuthAccessor} with the access token and token secret - * set. - * @throws OAuthProblemException if the request token in the accessor is not - * known. - */ - public OAuthAccessor generateAccessToken(String requestToken) throws OAuthProblemException { - OAuthAccessor accessor = getRequestTokenAccessor(requestToken); - - if (accessor.getProperty(USER_PROPERTY_NAME) == null) { - // User has not given the consumer permission yet. - throw OAuthUtil.newOAuthProblemException(OAuth.Problems.PERMISSION_UNKNOWN); - } - - // Token secret does not need to unique so can be generated now. - accessor.tokenSecret = generateToken(); - - do { - accessor.accessToken = generateToken(); - } while (accessTokenAccessors.putIfAbsent(accessor.accessToken, accessor) != null); - requestTokenAccessors.remove(accessor.requestToken); - - LOG.info("Generated access token for " + accessor.getProperty(USER_PROPERTY_NAME)); - return accessor.clone(); - } - - /** - * Generates an OAuth token. - */ - private String generateToken() { - return tokenGenerator.generateToken(TOKEN_LENGTH); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/operations/BlipOperationServices.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/operations/BlipOperationServices.java b/src/org/waveprotocol/box/server/robots/operations/BlipOperationServices.java deleted file mode 100644 index 767d7f9..0000000 --- a/src/org/waveprotocol/box/server/robots/operations/BlipOperationServices.java +++ /dev/null @@ -1,425 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.operations; - -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; -import com.google.wave.api.ApiIdSerializer; -import com.google.wave.api.BlipData; -import com.google.wave.api.Element; -import com.google.wave.api.InvalidRequestException; -import com.google.wave.api.OperationRequest; -import com.google.wave.api.OperationType; -import com.google.wave.api.JsonRpcConstant.ParamsProperty; -import com.google.wave.api.data.ApiView; -import com.google.wave.api.event.WaveletBlipCreatedEvent; - -import org.waveprotocol.box.server.robots.OperationContext; -import org.waveprotocol.box.server.robots.util.ConversationUtil; -import org.waveprotocol.box.server.robots.util.OperationUtil; -import org.waveprotocol.wave.model.conversation.ConversationBlip; -import org.waveprotocol.wave.model.conversation.ObservableConversation; -import org.waveprotocol.wave.model.conversation.ObservableConversationBlip; -import org.waveprotocol.wave.model.conversation.ObservableConversationView; -import org.waveprotocol.wave.model.conversation.WaveletBasedConversation; -import org.waveprotocol.wave.model.document.Doc; -import org.waveprotocol.wave.model.document.Document; -import org.waveprotocol.wave.model.document.util.LineContainers; -import org.waveprotocol.wave.model.document.util.Point; -import org.waveprotocol.wave.model.document.util.XmlStringBuilder; -import org.waveprotocol.wave.model.id.InvalidIdException; -import org.waveprotocol.wave.model.wave.ObservableWavelet; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet; - -/** - * {@link OperationService} for methods that create or deletes a blip. - * - * <p> - * These methods are: - * <li>{@link OperationType#BLIP_CONTINUE_THREAD}</li> - * <li>{@link OperationType#BLIP_CREATE_CHILD}</li> - * <li>{@link OperationType#WAVELET_APPEND_BLIP}</li> - * <li>{@link OperationType#DOCUMENT_APPEND_INLINE_BLIP}</li> - * <li>{@link OperationType#DOCUMENT_APPEND_MARKUP}</li> - * <li>{@link OperationType#DOCUMENT_INSERT_INLINE_BLIP}</li> - * <li>{@link OperationType#DOCUMENT_INSERT_INLINE_BLIP_AFTER_ELEMENT}</li> - * <li>{@link OperationType#BLIP_DELETE}</li>. - * - * @author [email protected] (Lennard de Rijk) - */ -public class BlipOperationServices implements OperationService { - - private BlipOperationServices() { - } - - @Override - public void execute( - OperationRequest operation, OperationContext context, ParticipantId participant) - throws InvalidRequestException { - OpBasedWavelet wavelet = context.openWavelet(operation, participant); - ObservableConversationView conversationView = context.openConversation(operation, participant); - - String waveletId = OperationUtil.getRequiredParameter(operation, ParamsProperty.WAVELET_ID); - String conversationId; - - try { - // TODO(anorth): Remove this round-trip when the API instead talks about - // opaque conversation ids, and doesn't use legacy id serialization. - conversationId = WaveletBasedConversation.idFor( - ApiIdSerializer.instance().deserialiseWaveletId(waveletId)); - } catch (InvalidIdException e) { - throw new InvalidRequestException("Invalid conversation id", operation, e); - } - ObservableConversation conversation = conversationView.getConversation(conversationId); - - OperationType type = OperationUtil.getOperationType(operation); - switch (type) { - case BLIP_CONTINUE_THREAD: - continueThread(operation, context, participant, conversation); - break; - case BLIP_CREATE_CHILD: - createChild(operation, context, participant, conversation); - break; - case WAVELET_APPEND_BLIP: - appendBlip(operation, context, participant, conversation); - break; - case DOCUMENT_APPEND_INLINE_BLIP: - appendInlineBlip(operation, context, participant, wavelet, conversation); - break; - case DOCUMENT_APPEND_MARKUP: - appendMarkup(operation, context, participant, wavelet, conversation); - break; - case DOCUMENT_INSERT_INLINE_BLIP: - insertInlineBlip(operation, context, participant, wavelet, conversation); - break; - case DOCUMENT_INSERT_INLINE_BLIP_AFTER_ELEMENT: - insertInlineBlipAfterElement(operation, context, participant, wavelet, conversation); - break; - case BLIP_DELETE: - delete(operation, context, participant, conversation); - break; - default: - throw new UnsupportedOperationException( - "This OperationService does not implement operation of type " + type.method()); - } - } - - /** - * Implementation of the {@link OperationType#BLIP_CONTINUE_THREAD} method. It - * appends a new blip to the end of the thread of the blip specified in the - * operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void continueThread(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.BLIP_CONTINUE_THREAD, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - String parentBlipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip parentBlip = context.getBlip(conversation, parentBlipId); - - ConversationBlip newBlip = parentBlip.getThread().appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation of the {@link OperationType#BLIP_CREATE_CHILD} method. It - * appends a new reply thread to the blip specified in the operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void createChild(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.BLIP_CREATE_CHILD, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - String parentBlipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip parentBlip = context.getBlip(conversation, parentBlipId); - - ConversationBlip newBlip = parentBlip.addReplyThread().appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation for the {@link OperationType#WAVELET_APPEND_BLIP} method. It - * appends a blip at the end of the root thread. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void appendBlip(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.WAVELET_APPEND_BLIP, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - - ObservableConversationBlip newBlip = conversation.getRootThread().appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation for the {@link OperationType#DOCUMENT_APPEND_INLINE_BLIP} - * method. It appends an inline blip on a new line in the blip specified in - * the operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param wavelet the wavelet to operate on. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void appendInlineBlip(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableWavelet wavelet, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.DOCUMENT_APPEND_INLINE_BLIP, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - String parentBlipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip parentBlip = context.getBlip(conversation, parentBlipId); - - // Append a new, empty line to the doc for the inline anchor. - Document doc = parentBlip.getContent(); - Doc.E line = LineContainers.appendLine(doc, XmlStringBuilder.createEmpty()); - - // Insert new inline thread with the blip at the empty sentence. - int location = doc.getLocation(Point.after(doc, line)); - ConversationBlip newBlip = parentBlip.addReplyThread(location).appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation for the {@link OperationType#DOCUMENT_APPEND_MARKUP} - * method. It appends markup within the blip specified in - * the operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param wavelet the wavelet to operate on. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void appendMarkup(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableWavelet wavelet, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.DOCUMENT_APPEND_MARKUP, - "Unsupported operation " + operation); - - String content = OperationUtil.getRequiredParameter(operation, ParamsProperty.CONTENT); - String blipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip convBlip = context.getBlip(conversation, blipId); - - // Create builder from xml content. - XmlStringBuilder markupBuilder = XmlStringBuilder.createFromXmlString(content); - - // Append the new markup to the blip doc. - Document doc = convBlip.getContent(); - LineContainers.appendLine(doc, markupBuilder); - - // Report success. - context.constructResponse(operation, Maps.<ParamsProperty, Object> newHashMap()); - } - - /** - * Implementation for the {@link OperationType#DOCUMENT_INSERT_INLINE_BLIP} - * method. It inserts an inline blip at the location specified in the - * operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param wavelet the wavelet to operate on. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void insertInlineBlip(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableWavelet wavelet, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.DOCUMENT_INSERT_INLINE_BLIP, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - String parentBlipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip parentBlip = context.getBlip(conversation, parentBlipId); - - Integer index = OperationUtil.getRequiredParameter(operation, ParamsProperty.INDEX); - if (index <= 0) { - throw new InvalidRequestException( - "Can't inline a blip on position <= 0, got " + index, operation); - } - - ApiView view = new ApiView(parentBlip.getContent(), wavelet); - int xmlLocation = view.transformToXmlOffset(index); - - // Insert new inline thread with the blip at the location as specified. - ConversationBlip newBlip = parentBlip.addReplyThread(xmlLocation).appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation for the - * {@link OperationType#DOCUMENT_INSERT_INLINE_BLIP_AFTER_ELEMENT} method. It - * inserts an inline blip after the element specified in the operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param wavelet the wavelet to operate on. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void insertInlineBlipAfterElement(OperationRequest operation, OperationContext context, - ParticipantId participant, OpBasedWavelet wavelet, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument(OperationUtil.getOperationType(operation) - == OperationType.DOCUMENT_INSERT_INLINE_BLIP_AFTER_ELEMENT, - "Unsupported operation " + operation); - - BlipData blipData = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_DATA); - String parentBlipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - ConversationBlip parentBlip = context.getBlip(conversation, parentBlipId); - - Element element = OperationUtil.getRequiredParameter(operation, ParamsProperty.ELEMENT); - - // view.locateElement will tell where the element actually is. - ApiView view = new ApiView(parentBlip.getContent(), wavelet); - int elementApiLocation = view.locateElement(element); - - if (elementApiLocation == -1) { - throw new InvalidRequestException("Requested element not found", operation); - } - - // Insert just after the requested element - int xmlLocation = view.transformToXmlOffset(elementApiLocation + 1); - - // Insert new inline thread with the blip at the location of the element. - ConversationBlip newBlip = parentBlip.addReplyThread(xmlLocation).appendBlip(); - context.putBlip(blipData.getBlipId(), newBlip); - - putContentForNewBlip(newBlip, blipData.getContent()); - processBlipCreatedEvent(operation, context, participant, conversation, newBlip); - } - - /** - * Implementation for the {@link OperationType#BLIP_DELETE} method. It deletes - * the blip specified in the operation. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @param conversation the conversation to operate on. - * @throws InvalidRequestException if the operation fails to perform - */ - private void delete(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableConversation conversation) - throws InvalidRequestException { - Preconditions.checkArgument( - OperationUtil.getOperationType(operation) == OperationType.BLIP_DELETE, - "Unsupported operation " + operation); - - String blipId = OperationUtil.getRequiredParameter(operation, ParamsProperty.BLIP_ID); - context.getBlip(conversation, blipId).delete(); - // report success. - context.constructResponse(operation, Maps.<ParamsProperty, Object> newHashMap()); - } - - /** - * Inserts content into the new blip. - * - * @param newBlip the newly created blip. - * @param content the content to add. - */ - private void putContentForNewBlip(ConversationBlip newBlip, String content) { - if (content.length() > 0 && content.charAt(0) == '\n') { - // While the client libraries force a newline to be sent as the first - // character we'll remove it here since the new blip we created already - // contains a newline. - content = content.substring(1); - } - XmlStringBuilder builder = XmlStringBuilder.createText(content); - LineContainers.appendToLastLine(newBlip.getContent(), builder); - } - - /** - * Processes a {@link WaveletBlipCreatedEvent} and puts it into the context. - * - * @param operation the operation that has been performed - * @param context the context of the operation. - * @param participant the participant performing the operation. - * @param conversation the conversation to which the new blip was added - * @param newBlip the newly created blip. - * @throws InvalidRequestException if the event could not be processed. - */ - private void processBlipCreatedEvent(OperationRequest operation, OperationContext context, - ParticipantId participant, ObservableConversation conversation, ConversationBlip newBlip) - throws InvalidRequestException { - WaveletBlipCreatedEvent event = - new WaveletBlipCreatedEvent(null, null, participant.getAddress(), - System.currentTimeMillis(), ConversationUtil.getRootBlipId(conversation), - newBlip.getId()); - context.processEvent(operation, event); - } - - public static BlipOperationServices create() { - return new BlipOperationServices(); - } -} http://git-wip-us.apache.org/repos/asf/incubator-wave/blob/7d8609e7/src/org/waveprotocol/box/server/robots/operations/CreateWaveletService.java ---------------------------------------------------------------------- diff --git a/src/org/waveprotocol/box/server/robots/operations/CreateWaveletService.java b/src/org/waveprotocol/box/server/robots/operations/CreateWaveletService.java deleted file mode 100644 index 963d52b..0000000 --- a/src/org/waveprotocol/box/server/robots/operations/CreateWaveletService.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.waveprotocol.box.server.robots.operations; - -import static org.waveprotocol.box.server.robots.util.RobotsUtil.createEmptyRobotWavelet; - -import com.google.common.collect.Lists; -import com.google.wave.api.ApiIdSerializer; -import com.google.wave.api.InvalidRequestException; -import com.google.wave.api.JsonRpcConstant.ParamsProperty; -import com.google.wave.api.OperationRequest; -import com.google.wave.api.event.WaveletCreatedEvent; -import com.google.wave.api.impl.WaveletData; - -import org.waveprotocol.box.server.robots.OperationContext; -import org.waveprotocol.box.server.robots.RobotWaveletData; -import org.waveprotocol.box.server.robots.util.OperationUtil; -import org.waveprotocol.wave.model.conversation.ObservableConversationBlip; -import org.waveprotocol.wave.model.conversation.ObservableConversationView; -import org.waveprotocol.wave.model.conversation.WaveletBasedConversation; -import org.waveprotocol.wave.model.id.InvalidIdException; -import org.waveprotocol.wave.model.id.WaveId; -import org.waveprotocol.wave.model.id.WaveletId; -import org.waveprotocol.wave.model.id.WaveletName; -import org.waveprotocol.wave.model.wave.InvalidParticipantAddress; -import org.waveprotocol.wave.model.wave.ParticipantId; -import org.waveprotocol.wave.model.wave.opbased.OpBasedWavelet; - -import java.util.List; - -/** - * Implementation of the "wavelet.create" and "robot.createwavelet" operations. - * - * @author [email protected] (Lennard de Rijk) - */ -public class CreateWaveletService implements OperationService { - - private CreateWaveletService() { - } - - /** - * Creates a new wavelet that is conversational based on the given - * {@link WaveletData}. Note that this is the robot api version of wavelet - * data not to be confused with - * {@link org.waveprotocol.wave.model.wave.data.WaveletData}. - * - * <p> - * The wavelet data must define the wave id in the temporary format otherwise - * it can not be opened by subsequent operations when calling - * {@link OperationContext#openWavelet(String, String, ParticipantId)} with - * the temporary wave id and wavelet id. - * - * @param operation the operation to execute. - * @param context the context of the operation. - * @param participant the participant performing this operation. - * @throws InvalidRequestException if the operation fails to perform. - */ - @Override - public void execute( - OperationRequest operation, OperationContext context, ParticipantId participant) - throws InvalidRequestException { - WaveletData waveletData = - OperationUtil.getRequiredParameter(operation, ParamsProperty.WAVELET_DATA); - - // The loop validates the addresses present in the wavelet data before - // creating a new wavelet. - List<ParticipantId> participants = Lists.newArrayList(participant); - for (String address : waveletData.getParticipants()) { - try { - participants.add(ParticipantId.of(address)); - } catch (InvalidParticipantAddress e) { - throw new InvalidRequestException( - address + " is not a valid participant address", operation); - } - } - - WaveletName waveletName = context.getConversationUtil().generateWaveletName(); - RobotWaveletData newWavelet = createEmptyRobotWavelet(participant, waveletName); - OpBasedWavelet opBasedWavelet = newWavelet.getOpBasedWavelet(participant); - - WaveletBasedConversation.makeWaveletConversational(opBasedWavelet); - - ObservableConversationView conversation = - context.getConversationUtil().buildConversation(opBasedWavelet); - ObservableConversationBlip rootBlip = conversation.getRoot().getRootThread().appendBlip(); - - for (ParticipantId newParticipant : participants) { - opBasedWavelet.addParticipant(newParticipant); - } - - // Store the temporary id of the wavelet and rootblip so that future - // operations can reference it. - try { - WaveId waveId = ApiIdSerializer.instance().deserialiseWaveId(waveletData.getWaveId()); - WaveletId waveletId = - ApiIdSerializer.instance().deserialiseWaveletId(waveletData.getWaveletId()); - context.putWavelet(waveId, waveletId, newWavelet); - } catch (InvalidIdException e) { - throw new InvalidRequestException("Invalid id", operation, e); - } - context.putBlip(waveletData.getRootBlipId(), rootBlip); - - String message = OperationUtil.getOptionalParameter(operation, ParamsProperty.MESSAGE); - WaveletCreatedEvent event = - new WaveletCreatedEvent(null, null, participant.getAddress(), System.currentTimeMillis(), - rootBlip.getId(), message, - ApiIdSerializer.instance().serialiseWaveId(waveletName.waveId), - ApiIdSerializer.instance().serialiseWaveletId(waveletName.waveletId)); - context.processEvent(operation, event); - } - - public static CreateWaveletService create() { - return new CreateWaveletService(); - } -}
