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