Merge with 2ad3c504ee5d73982c0ef23336276dc1fc9e165f
[pithos] / src / gr / ebs / gss / server / rest / GroupsHandler.java
1 /*
2  * Copyright 2008, 2009 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server.rest;
20
21 import gr.ebs.gss.client.exceptions.DuplicateNameException;
22 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
23 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
24 import gr.ebs.gss.client.exceptions.RpcException;
25 import gr.ebs.gss.server.domain.Group;
26 import gr.ebs.gss.server.domain.User;
27 import gr.ebs.gss.server.ejb.TransactionHelper;
28
29 import java.io.IOException;
30 import java.net.URLDecoder;
31 import java.net.URLEncoder;
32 import java.util.List;
33 import java.util.concurrent.Callable;
34
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.json.JSONArray;
41 import org.json.JSONException;
42 import org.json.JSONObject;
43
44
45 /**
46  * A class that handles operations on the 'groups' namespace.
47  *
48  * @author past
49  */
50 public class GroupsHandler extends RequestHandler {
51         /**
52          * The logger.
53          */
54         private static Log logger = LogFactory.getLog(GroupsHandler.class);
55
56     /**
57      * Serve the groups namespace for the user.
58      *
59      * @param req The servlet request we are processing
60      * @param resp The servlet response we are processing
61      * @throws IOException if an input/output error occurs
62          */
63         void serveGroups(HttpServletRequest req, HttpServletResponse resp) throws IOException {
64         String parentUrl = getContextPath(req, true);
65         String path = getInnerPath(req, PATH_GROUPS);
66                 if (path.equals(""))
67                         path = "/";
68
69         User user = getUser(req);
70         User owner = getOwner(req);
71         if (!owner.equals(user)) {
72                 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
73                 return;
74         }
75         if (path.equals("/"))
76                         // Request to serve all groups
77                 try {
78                 List<Group> groups = getService().getGroups(owner.getId());
79                 JSONArray json = new JSONArray();
80                 for (Group group: groups) {
81                         JSONObject j = new JSONObject();
82                         j.put("name", group.getName()).
83                                 put("uri", parentUrl + URLEncoder.encode(group.getName(),"UTF-8"));
84                                 json.put(j);
85                 }
86
87                 sendJson(req, resp, json.toString());
88                 } catch (ObjectNotFoundException e) {
89                         logger.error("User not found", e);
90                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
91                         return;
92                 } catch (RpcException e) {
93                         logger.error("", e);
94                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
95                         return;
96                 } catch (JSONException e) {
97                         logger.error("", e);
98                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
99                         return;
100                         }
101                 else {
102                 // Chop any trailing slash
103                 path = path.endsWith("/")? path.substring(0, path.length()-1): path;
104                 // Chop any leading slash
105                 path = path.startsWith("/")? path.substring(1): path;
106                 int slash = path.indexOf('/');
107                         try {
108                         if (slash != -1) {
109                                 // Request to serve group member
110                                 if (logger.isDebugEnabled())
111                                         logger.debug("Serving member " + path.substring(slash + 1) +
112                                                                 " from group " + path.substring(0, slash));
113                                 Group group = getService().getGroup(owner.getId(), URLDecoder.decode(path.substring(0, slash),"UTF-8"));
114                                 group = getService().expandGroup(group);
115                                 for (User u: group.getMembers())
116                                         if (u.getUsername().equals(path.substring(slash + 1))) {
117                                                 // Build the proper parent URL
118                                                 String pathInfo = req.getPathInfo();
119                                                 parentUrl = parentUrl.replaceFirst(pathInfo, "");
120                                         JSONObject json = new JSONObject();
121                                         json.put("username", u.getUsername()).put("name", u.getName()).
122                                                         put("home", parentUrl + u.getUsername());
123
124                                         sendJson(req, resp, json.toString());
125                                         }
126                         } else {
127                                 // Request to serve group
128                                 if (logger.isDebugEnabled())
129                                         logger.debug("Serving group " + path);
130                                 Group group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
131                                 group = getService().expandGroup(group);
132                                 JSONArray json = new JSONArray();
133                                 for (User u: group.getMembers())
134                                         json.put(parentUrl + u.getUsername());
135
136                                 sendJson(req, resp, json.toString());
137                         }
138                         } catch (ObjectNotFoundException e) {
139                         logger.error("", e);
140                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
141                         return;
142                         } catch (RpcException e) {
143                         logger.error("", e);
144                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
145                         return;
146                         } catch (JSONException e) {
147                                 logger.error("", e);
148                                 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
149                                 return;
150                         }
151         }
152         // Workaround for IE's broken caching behavior.
153                 resp.setHeader("Expires", "-1");
154         }
155
156         /**
157          * Handle POST requests in the groups namespace.
158          *
159      * @param req The servlet request we are processing
160      * @param resp The servlet response we are processing
161      * @throws IOException if an input/output error occurs
162          */
163         void postGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException {
164         // Identify the requested resource path
165         String path = getInnerPath(req, PATH_GROUPS);
166                 if (path.equals(""))
167                         path = "/";
168
169                 try {
170                 User user = getUser(req);
171                 final User owner = getOwner(req);
172                 if (!owner.equals(user))
173                         throw new InsufficientPermissionsException("User " + user.getUsername()
174                                                 + " does not have permission to modify the groups owned by "
175                                                 + owner.getUsername());
176                 if (path.equals("/")) {
177                         // Request to add group
178                         final String group = req.getParameter(GROUP_PARAMETER);
179                         if (!isValidResourceName(group)) {
180                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
181                                 return;
182                         }
183                         if (logger.isDebugEnabled())
184                                 logger.debug("Adding group " + group);
185                                 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
186                                                 @Override
187                                                 public Void call() throws Exception {
188                                                         getService().createGroup(owner.getId(), group);
189                                                         return null;
190                                                 }
191                                         });
192                                 resp.setStatus(HttpServletResponse.SC_CREATED);
193                 } else {
194                         // Request to add group member
195                         String username = req.getParameter(USERNAME_PARAMETER);
196                         if (!isValidResourceName(username)) {
197                                 resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
198                                 return;
199                         }
200                         // Chop any trailing slash
201                         path = path.endsWith("/")? path.substring(0, path.length()-1): path;
202                         // Chop any leading slash
203                         path = path.startsWith("/")? path.substring(1): path;
204                         if (logger.isDebugEnabled())
205                                 logger.debug("Adding member " + username +
206                                                         " to group " + path);
207                         final Group group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
208                         final User member = getService().findUser(username);
209                         if (member == null) {
210                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "User " + username + " not found");
211                                 return;
212                         }
213                         new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
214                                         @Override
215                                         public Void call() throws Exception {
216                                                 getService().addUserToGroup(owner.getId(), group.getId(), member.getId());
217                                                 return null;
218                                         }
219                                 });
220                         resp.setStatus(HttpServletResponse.SC_CREATED);
221                 }
222                 // Workaround for IE's broken caching behavior.
223                         resp.setHeader("Expires", "-1");
224                 } catch (ObjectNotFoundException e) {
225                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
226                 } catch (IllegalArgumentException e) {
227                         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
228                 } catch (DuplicateNameException e) {
229                         resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
230                 } catch (RpcException e) {
231                         logger.error("", e);
232                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
233                 } catch (InsufficientPermissionsException e) {
234                         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, e.getMessage());
235                 } catch (Exception e) {
236                         logger.error("", e);
237                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
238                 }
239
240         }
241
242         /**
243          * Handle DELETE requests in the groups namespace.
244          *
245      * @param req The servlet request we are processing
246      * @param resp The servlet response we are processing
247      * @throws IOException if an input/output error occurs
248          */
249         void deleteGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException {
250         String path = getInnerPath(req, PATH_GROUPS);
251                 if (path.equals(""))
252                         path = "/";
253
254         if (path.equals("/"))
255                         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, METHOD_GET + ", " + METHOD_POST);
256                 else {
257                 // Chop any trailing slash
258                 path = path.endsWith("/")? path.substring(0, path.length()-1): path;
259                 // Chop any leading slash
260                 path = path.startsWith("/")? path.substring(1): path;
261                 int slash = path.indexOf('/');
262                 try {
263                 User user = getUser(req);
264                 final User owner = getOwner(req);
265                 if (!owner.equals(user))
266                         throw new InsufficientPermissionsException("User " + user.getUsername()
267                                                 + " does not have permission to modify the groups owned by "
268                                                 + owner.getUsername());
269                 if (slash != -1) {
270                         // Request to delete group member
271                         if (logger.isDebugEnabled())
272                                 logger.debug("Removing member " + path.substring(slash + 1) +
273                                                         " from group " + path.substring(0, slash));
274                         final Group group = getService().getGroup(owner.getId(), URLDecoder.decode(path.substring(0, slash),"UTF-8"));
275                         for (final User u: group.getMembers())
276                                 if (u.getUsername().equals(path.substring(slash + 1)))
277                                         new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
278                                                 @Override
279                                                 public Void call() throws Exception {
280                                                         getService().removeMemberFromGroup(owner.getId(), group.getId(), u.getId());
281                                                         return null;
282                                                 }
283                                         });
284                 } else {
285                         if (logger.isDebugEnabled())
286                                 logger.debug("Removing group " + path);
287                                 final Group group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
288                                 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
289                                         @Override
290                                         public Void call() throws Exception {
291                                                 getService().deleteGroup(owner.getId(), group.getId());
292                                                 return null;
293                                         }
294                                 });
295                 }
296                         resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
297                         // Workaround for IE's broken caching behavior.
298                         resp.setHeader("Expires", "-1");
299                 } catch (RpcException e) {
300                         logger.error("", e);
301                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
302                 } catch (ObjectNotFoundException e) {
303                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
304                         } catch (InsufficientPermissionsException e) {
305                                 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, e.getMessage());
306                         } catch (Exception e) {
307                         logger.error("", e);
308                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
309                         }
310         }
311         }
312
313 }