PageRenderTime 21ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/plugin/src/main/java/com/atlassian/labs/speakeasy/rest/PluginsResource.java

https://github.com/mrdon/speakeasy-plugin
Java | 352 lines | 317 code | 32 blank | 3 comment | 8 complexity | a3bcc3950101280558a301a58e44072e MD5 | raw file
  1. package com.atlassian.labs.speakeasy.rest;
  2. import com.atlassian.labs.speakeasy.external.PluginType;
  3. import com.atlassian.labs.speakeasy.external.SpeakeasyService;
  4. import com.atlassian.labs.speakeasy.external.UnauthorizedAccessException;
  5. import com.atlassian.labs.speakeasy.manager.PluginOperationFailedException;
  6. import com.atlassian.labs.speakeasy.model.*;
  7. import com.atlassian.plugins.rest.common.json.JaxbJsonMarshaller;
  8. import com.atlassian.plugins.rest.common.security.RequiresXsrfCheck;
  9. import com.atlassian.sal.api.user.UserManager;
  10. import com.atlassian.sal.api.xsrf.XsrfTokenValidator;
  11. import org.apache.commons.fileupload.FileItem;
  12. import org.apache.commons.fileupload.FileUploadException;
  13. import org.apache.commons.fileupload.disk.DiskFileItemFactory;
  14. import org.apache.commons.fileupload.servlet.ServletFileUpload;
  15. import org.json.JSONException;
  16. import org.json.JSONObject;
  17. import org.slf4j.Logger;
  18. import org.slf4j.LoggerFactory;
  19. import javax.servlet.http.HttpServletRequest;
  20. import javax.ws.rs.Consumes;
  21. import javax.ws.rs.DELETE;
  22. import javax.ws.rs.FormParam;
  23. import javax.ws.rs.GET;
  24. import javax.ws.rs.POST;
  25. import javax.ws.rs.PUT;
  26. import javax.ws.rs.Path;
  27. import javax.ws.rs.PathParam;
  28. import javax.ws.rs.Produces;
  29. import javax.ws.rs.QueryParam;
  30. import javax.ws.rs.core.Context;
  31. import javax.ws.rs.core.Response;
  32. import java.io.File;
  33. import java.net.URI;
  34. import java.net.URISyntaxException;
  35. import java.util.List;
  36. import static com.atlassian.labs.speakeasy.util.KeyExtractor.createExtractableTempFile;
  37. import static com.atlassian.labs.speakeasy.util.KeyExtractor.extractFromFilename;
  38. /**
  39. *
  40. */
  41. @Path("/plugins")
  42. public class PluginsResource
  43. {
  44. private final SpeakeasyService speakeasyService;
  45. private final UserManager userManager;
  46. private final JaxbJsonMarshaller jaxbJsonMarshaller;
  47. private final XsrfTokenValidator xsrfTokenValidator;
  48. private static final Logger log = LoggerFactory.getLogger(PluginsResource.class);
  49. public PluginsResource(UserManager userManager, JaxbJsonMarshaller jaxbJsonMarshaller, SpeakeasyService speakeasyService, XsrfTokenValidator xsrfTokenValidator)
  50. {
  51. this.userManager = userManager;
  52. this.jaxbJsonMarshaller = jaxbJsonMarshaller;
  53. this.speakeasyService = speakeasyService;
  54. this.xsrfTokenValidator = xsrfTokenValidator;
  55. }
  56. @GET
  57. @Path("atom")
  58. @Produces("application/atom+xml")
  59. public Response atom() throws UnauthorizedAccessException
  60. {
  61. String user = userManager.getRemoteUsername();
  62. return Response.ok().entity(speakeasyService.getPluginFeed(user)).build();
  63. }
  64. @DELETE
  65. @Path("plugin/{pluginKey}")
  66. @Produces("application/json")
  67. public Response uninstallPlugin(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  68. {
  69. String user = userManager.getRemoteUsername();
  70. UserPlugins entity = speakeasyService.uninstallPlugin(pluginKey, user);
  71. return Response.ok().entity(entity).build();
  72. }
  73. @GET
  74. @Path("download/project/{pluginKey}-project.zip")
  75. @Produces("application/octet-stream")
  76. public Response getAsAmpsProject(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  77. {
  78. String user = userManager.getRemoteUsername();
  79. File file = speakeasyService.getPluginAsProject(pluginKey, user);
  80. return Response.ok().entity(file).build();
  81. }
  82. @GET
  83. @Path("screenshot/{pluginKey}.png")
  84. @Produces("image/png")
  85. public Response getScreenshot(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException, URISyntaxException
  86. {
  87. String user = userManager.getRemoteUsername();
  88. String url = speakeasyService.getScreenshotUrl(pluginKey, user);
  89. return Response.status(301).location(new URI(url)).build();
  90. }
  91. @GET
  92. @Path("download/extension/{pluginKeyAndExtension}")
  93. @Produces("application/octet-stream")
  94. public Response getAsExtension(@PathParam("pluginKeyAndExtension") String pluginKeyAndExtension) throws UnauthorizedAccessException
  95. {
  96. String user = userManager.getRemoteUsername();
  97. int pos = pluginKeyAndExtension.lastIndexOf('.');
  98. if (pos > 0)
  99. {
  100. File file = speakeasyService.getPluginArtifact(pluginKeyAndExtension.substring(0, pos), user);
  101. return Response.ok().entity(file).build();
  102. }
  103. else
  104. {
  105. throw new PluginOperationFailedException("Missing extension on '" + pluginKeyAndExtension, null);
  106. }
  107. }
  108. @POST
  109. @Path("search")
  110. @Produces("application/json")
  111. @RequiresXsrfCheck
  112. public Response search(@FormParam("q") String searchQuery) throws UnauthorizedAccessException
  113. {
  114. SearchResults entity = speakeasyService.search(searchQuery, userManager.getRemoteUsername());
  115. return Response.ok().entity(entity).build();
  116. }
  117. @POST
  118. @Path("fork/{pluginKey}")
  119. @Produces("application/json")
  120. @RequiresXsrfCheck
  121. public Response fork(@PathParam("pluginKey") String pluginKey, @FormParam("description") String description) throws UnauthorizedAccessException
  122. {
  123. UserPlugins entity = speakeasyService.fork(pluginKey, userManager.getRemoteUsername(), description);
  124. return Response.ok().entity(entity).build();
  125. }
  126. @POST
  127. @Path("feedback/{pluginKey}")
  128. @Produces("application/json")
  129. @RequiresXsrfCheck
  130. public Response feedback(@PathParam("pluginKey") String pluginKey, Feedback feedback) throws UnauthorizedAccessException
  131. {
  132. speakeasyService.sendFeedback(pluginKey, feedback, userManager.getRemoteUsername());
  133. return Response.ok().build();
  134. }
  135. @POST
  136. @Path("broken/{pluginKey}")
  137. @Produces("application/json")
  138. @RequiresXsrfCheck
  139. public Response broken(@PathParam("pluginKey") String pluginKey, Feedback feedback) throws UnauthorizedAccessException
  140. {
  141. speakeasyService.reportBroken(pluginKey, feedback, userManager.getRemoteUsername());
  142. return Response.ok().build();
  143. }
  144. @POST
  145. @Path("favorite/{pluginKey}")
  146. @Produces("application/json")
  147. @RequiresXsrfCheck
  148. public Response favorite(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  149. {
  150. UserPlugins entity = speakeasyService.favorite(pluginKey, userManager.getRemoteUsername());
  151. return Response.ok().entity(entity).build();
  152. }
  153. @DELETE
  154. @Path("favorite/{pluginKey}")
  155. @Produces("application/json")
  156. public Response unfavorite(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  157. {
  158. UserPlugins entity = speakeasyService.unfavorite(pluginKey, userManager.getRemoteUsername());
  159. return Response.ok().entity(entity).build();
  160. }
  161. @POST
  162. @Path("create/{pluginKey}")
  163. @Produces("application/json")
  164. @RequiresXsrfCheck
  165. public Response create(@PathParam("pluginKey") String pluginKey, @FormParam("description") String description, @FormParam("name") String name) throws UnauthorizedAccessException
  166. {
  167. UserPlugins entity = speakeasyService.createExtension(pluginKey, PluginType.ZIP, userManager.getRemoteUsername(), description, name);
  168. return Response.ok().entity(entity).build();
  169. }
  170. @DELETE
  171. @Path("global/{pluginKey}")
  172. @Produces("application/json")
  173. public Response disableGlobally(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  174. {
  175. UserPlugins entity = speakeasyService.disableGlobally(pluginKey, userManager.getRemoteUsername());
  176. return Response.ok().entity(entity).build();
  177. }
  178. @PUT
  179. @Path("global/{pluginKey}")
  180. @Produces("application/json")
  181. @RequiresXsrfCheck
  182. public Response enableGlobally(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  183. {
  184. UserPlugins entity = speakeasyService.enableGlobally(pluginKey, userManager.getRemoteUsername());
  185. return Response.ok().entity(entity).build();
  186. }
  187. @GET
  188. @Path("plugin/{pluginKey}/index")
  189. @Produces("application/json")
  190. public Response getIndex(@PathParam("pluginKey") String pluginKey) throws UnauthorizedAccessException
  191. {
  192. String user = userManager.getRemoteUsername();
  193. PluginIndex index = new PluginIndex();
  194. index.setFiles(speakeasyService.getPluginFileNames(pluginKey, user));
  195. return Response.ok().entity(index).build();
  196. }
  197. @GET
  198. @Path("plugin/{pluginKey}/file")
  199. @Produces("text/plain")
  200. public Response getFileText(@PathParam("pluginKey") String pluginKey, @QueryParam("path") String fileName) throws UnauthorizedAccessException
  201. {
  202. String user = userManager.getRemoteUsername();
  203. Object pluginFile = speakeasyService.getPluginFile(pluginKey, fileName, user);
  204. return Response.ok().entity(pluginFile).build();
  205. }
  206. @GET
  207. @Path("plugin/{pluginKey}/binary")
  208. @Produces("application/octet-stream")
  209. public Response getFileBinary(@PathParam("pluginKey") String pluginKey, @QueryParam("path") String fileName) throws UnauthorizedAccessException
  210. {
  211. String user = userManager.getRemoteUsername();
  212. Object pluginFile = speakeasyService.getPluginFile(pluginKey, fileName, user);
  213. return Response.ok().entity(pluginFile).build();
  214. }
  215. @PUT
  216. @Path("plugin/{pluginKey}/file")
  217. @Consumes("text/plain")
  218. @Produces("application/json")
  219. public Response saveAndRebuild(@PathParam("pluginKey") String pluginKey, @QueryParam("path") String fileName, String contents) throws UnauthorizedAccessException
  220. {
  221. String user = userManager.getRemoteUsername();
  222. final UserExtension extension = speakeasyService.saveAndRebuild(pluginKey, fileName, contents, user);
  223. return Response.ok().entity(extension).build();
  224. }
  225. @POST
  226. @Produces("text/html")
  227. public Response uploadPlugin(@Context HttpServletRequest request)
  228. {
  229. String user = userManager.getRemoteUsername(request);
  230. try
  231. {
  232. xsrfTokenValidator.validateFormEncodedToken(request);
  233. File uploadedFile = extractPluginFile(request);
  234. UserPlugins plugins = speakeasyService.installPlugin(uploadedFile, user);
  235. return Response.ok().entity(wrapBodyInTextArea(jaxbJsonMarshaller.marshal(plugins))).build();
  236. }
  237. catch (PluginOperationFailedException e)
  238. {
  239. log.error(e.getError(), e.getCause());
  240. return Response.ok().entity(wrapBodyInTextArea(createErrorJson(user, e))).build();
  241. }
  242. catch (UnauthorizedAccessException e)
  243. {
  244. return Response.ok().entity(wrapBodyInTextArea(createErrorJson(user, e))).build();
  245. }
  246. catch (RuntimeException e)
  247. {
  248. log.error(e.getMessage(), e);
  249. return Response.ok().entity(wrapBodyInTextArea(createErrorJson(user, e))).build();
  250. }
  251. }
  252. private String createErrorJson(String user, Exception e)
  253. {
  254. JSONObject obj = new JSONObject();
  255. try
  256. {
  257. obj.put("error", e.toString());
  258. obj.put("plugins", new JSONObject(jaxbJsonMarshaller.marshal(speakeasyService.getRemotePluginList(user))));
  259. }
  260. catch (JSONException e1)
  261. {
  262. throw new PluginOperationFailedException("Unable to serialize error", e1, null);
  263. }
  264. catch (UnauthorizedAccessException e1)
  265. {
  266. throw new PluginOperationFailedException("Unauthorized access", e1, null);
  267. }
  268. return obj.toString();
  269. }
  270. private File extractPluginFile(HttpServletRequest request)
  271. {
  272. if (!ServletFileUpload.isMultipartContent(request))
  273. {
  274. return null;
  275. }
  276. DiskFileItemFactory factory = new DiskFileItemFactory();
  277. factory.setSizeThreshold(1024 * 1024);
  278. ServletFileUpload upload = new ServletFileUpload(factory);
  279. upload.setFileSizeMax(1024 * 1024 * 10);
  280. List<FileItem> items = null;
  281. try
  282. {
  283. items = upload.parseRequest(request);
  284. }
  285. catch (FileUploadException e)
  286. {
  287. throw new RuntimeException(e);
  288. }
  289. File pluginFile = null;
  290. if (items != null)
  291. {
  292. for (FileItem item : items)
  293. {
  294. if (!item.isFormField() && item.getSize() > 0 && "plugin-file".equals(item.getFieldName()))
  295. {
  296. try
  297. {
  298. String fileName = processFileName(item.getName());
  299. String key = extractFromFilename(fileName);
  300. pluginFile = createExtractableTempFile(key, fileName);
  301. item.write(pluginFile);
  302. }
  303. catch (Exception e)
  304. {
  305. throw new RuntimeException(e);
  306. }
  307. }
  308. }
  309. }
  310. return pluginFile;
  311. }
  312. private String wrapBodyInTextArea(String body)
  313. {
  314. return "JSON_MARKER||" + body + "||";
  315. }
  316. private String processFileName(String fileNameInput)
  317. {
  318. return fileNameInput.substring(fileNameInput.lastIndexOf("\\") + 1, fileNameInput.length());
  319. }
  320. }