View Javadoc
1   /*
2    * Licensed to The Apereo Foundation under one or more contributor license
3    * agreements. See the NOTICE file distributed with this work for additional
4    * information regarding copyright ownership.
5    *
6    *
7    * The Apereo Foundation licenses this file to you under the Educational
8    * Community License, Version 2.0 (the "License"); you may not use this file
9    * except in compliance with the License. You may obtain a copy of the License
10   * at:
11   *
12   *   http://opensource.org/licenses/ecl2.txt
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
17   * License for the specific language governing permissions and limitations under
18   * the License.
19   *
20   */
21  
22  package org.opencastproject.userdirectory.endpoint;
23  
24  import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
25  import static javax.servlet.http.HttpServletResponse.SC_CONFLICT;
26  import static javax.servlet.http.HttpServletResponse.SC_CREATED;
27  import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
28  import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
29  import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
30  import static javax.servlet.http.HttpServletResponse.SC_OK;
31  
32  import org.opencastproject.security.api.JaxbGroupList;
33  import org.opencastproject.security.api.UnauthorizedException;
34  import org.opencastproject.userdirectory.ConflictException;
35  import org.opencastproject.userdirectory.JpaGroupRoleProvider;
36  import org.opencastproject.util.NotFoundException;
37  import org.opencastproject.util.doc.rest.RestParameter;
38  import org.opencastproject.util.doc.rest.RestParameter.Type;
39  import org.opencastproject.util.doc.rest.RestQuery;
40  import org.opencastproject.util.doc.rest.RestResponse;
41  import org.opencastproject.util.doc.rest.RestService;
42  
43  import org.apache.commons.lang3.StringUtils;
44  import org.osgi.service.component.annotations.Activate;
45  import org.osgi.service.component.annotations.Component;
46  import org.osgi.service.component.annotations.Reference;
47  import org.osgi.service.jaxrs.whiteboard.propertytypes.JaxrsResource;
48  import org.slf4j.Logger;
49  import org.slf4j.LoggerFactory;
50  
51  import javax.ws.rs.DELETE;
52  import javax.ws.rs.FormParam;
53  import javax.ws.rs.GET;
54  import javax.ws.rs.POST;
55  import javax.ws.rs.PUT;
56  import javax.ws.rs.Path;
57  import javax.ws.rs.PathParam;
58  import javax.ws.rs.Produces;
59  import javax.ws.rs.QueryParam;
60  import javax.ws.rs.WebApplicationException;
61  import javax.ws.rs.core.MediaType;
62  import javax.ws.rs.core.Response;
63  
64  /**
65   * A REST EndPoint for JpaGroupRoleProvider.
66   */
67  @Path("/groups")
68  @RestService(
69      name = "groups",
70      title = "Internal group manager",
71      abstractText = "This service offers the ability to manage the groups for internal accounts.",
72      notes = {
73          "All paths above are relative to the REST endpoint base (something like http://your.server/files)",
74          "If the service is down or not working it will return a status 503, this means the the "
75              + "underlying service is not working and is either restarting or has failed",
76          "A status code 500 means a general failure has occurred which is not recoverable and was "
77              + "not anticipated. In other words, there is a bug! You should file an error report "
78              + "with your server logs from the time when the error occurred: "
79              + "<a href=\"https://github.com/opencast/opencast/issues\">Opencast Issue Tracker</a>"
80      }
81  )
82  @Component(
83      property = {
84          "service.description=Group Role REST EndPoint",
85          "opencast.service.type=org.opencastproject.userdirectory.endpoint.GroupRoleEndpoint",
86          "opencast.service.jobproducer=false",
87          "opencast.service.path=/groups"
88      },
89      immediate = false,
90      service = { GroupRoleEndpoint.class }
91  )
92  @JaxrsResource
93  public class GroupRoleEndpoint {
94  
95    /** The logger */
96    private static final Logger logger = LoggerFactory.getLogger(GroupRoleEndpoint.class);
97  
98    /** the jpaGroupRoleProvider Impl service */
99    private JpaGroupRoleProvider jpaGroupRoleProvider;
100 
101   /**
102    * @param jpaGroupRoleProvider
103    *          the jpaGroupRoleProvider to set
104    */
105   @Reference
106   public void setJpaGroupRoleProvider(JpaGroupRoleProvider jpaGroupRoleProvider) {
107     this.jpaGroupRoleProvider = jpaGroupRoleProvider;
108   }
109 
110   /**
111    * Callback for activation of this component.
112    */
113   @Activate
114   public void activate() {
115     logger.info("Activating  {}", getClass().getName());
116   }
117 
118   @GET
119   @Produces({ MediaType.TEXT_XML, MediaType.APPLICATION_JSON })
120   @Path("groups.{format:xml|json}")
121   @RestQuery(
122       name = "allgroup",
123       description = "Returns a list of groups",
124       returnDescription = "Returns a JSON or XML representation of the list of groups available "
125           + "the current user's organization",
126       pathParameters = {
127           @RestParameter(
128               name = "format",
129               description = "The output format (json or xml) of the response body.",
130               isRequired = true,
131               type = RestParameter.Type.STRING
132           )
133       },
134       restParameters = {
135           @RestParameter(
136               name = "limit",
137               defaultValue = "100",
138               description = "The maximum number of items to return per page.",
139               isRequired = false,
140               type = RestParameter.Type.STRING
141           ),
142           @RestParameter(
143               name = "offset",
144               defaultValue = "0",
145               description = "The page number.",
146               isRequired = false,
147               type = RestParameter.Type.STRING
148           )
149       },
150       responses = { @RestResponse(responseCode = SC_OK, description = "The groups.") }
151   )
152   public Response getGroupsAsJsonOrXml(
153       @PathParam("format") String format,
154       @QueryParam("limit") int limit,
155       @QueryParam("offset") int offset
156   ) {
157     try {
158       final String type = "json".equals(format) ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML;
159       JaxbGroupList list = jpaGroupRoleProvider.getGroups(limit, offset);
160       return Response.ok().entity(list).type(type).build();
161     } catch (Exception e) {
162       logger.info("Unable to get groups", e);
163       return Response.serverError().entity(buildUnexpectedErrorMessage(e)).build();
164     }
165   }
166 
167   @DELETE
168   @Path("{id}")
169   @RestQuery(
170       name = "removegroup",
171       description = "Remove a group",
172       returnDescription = "Return no content",
173       pathParameters = {
174           @RestParameter(
175               name = "id",
176               description = "The group identifier",
177               isRequired = true,
178               type = Type.STRING
179           ),
180       },
181       responses = {
182           @RestResponse(
183               responseCode = SC_OK,
184               description = "Group deleted"
185           ),
186           @RestResponse(
187               responseCode = SC_FORBIDDEN,
188               description = "Not enough permissions to remove a group with the admin role."
189           ),
190           @RestResponse(
191               responseCode = SC_NOT_FOUND,
192               description = "Group not found."
193           ),
194           @RestResponse(
195               responseCode = SC_INTERNAL_SERVER_ERROR,
196               description = "An internal server error occured."
197           ),
198       }
199   )
200   public Response removeGroup(@PathParam("id") String groupId) {
201     try {
202       jpaGroupRoleProvider.removeGroup(groupId);
203       return Response.noContent().build();
204     } catch (NotFoundException e) {
205       return Response.status(SC_NOT_FOUND).build();
206     } catch (UnauthorizedException e) {
207       return Response.status(SC_FORBIDDEN).build();
208     } catch (Exception e) {
209       throw new WebApplicationException(e);
210     }
211   }
212 
213   @POST
214   @Path("")
215   @RestQuery(
216       name = "createGroup",
217       description = "Add a group",
218       returnDescription = "Return the status codes",
219       restParameters = {
220           @RestParameter(
221               name = "name",
222               description = "The group name",
223               isRequired = true,
224               type = Type.STRING
225           ),
226           @RestParameter(
227               name = "description",
228               description = "The group description",
229               isRequired = false,
230               type = Type.STRING
231           ),
232           @RestParameter(
233               name = "roles",
234               description = "A comma seperated string of additional group roles",
235               isRequired = false,
236               type = Type.TEXT
237           ),
238           @RestParameter(
239               name = "users",
240               description = "A comma seperated string of group members",
241               isRequired = false,
242               type = Type.TEXT
243           ),
244       },
245       responses = {
246           @RestResponse(
247               responseCode = SC_CREATED,
248               description = "Group created"
249           ),
250           @RestResponse(
251               responseCode = SC_BAD_REQUEST,
252               description = "Name too long"
253           ),
254           @RestResponse(
255               responseCode = SC_FORBIDDEN,
256               description = "Not enough permissions to create a group with the admin role."
257           ),
258           @RestResponse(
259               responseCode = SC_CONFLICT,
260               description = "An group with this name already exists."
261           ),
262       }
263   )
264   public Response createGroup(
265       @FormParam("name") String name,
266       @FormParam("description") String description,
267       @FormParam("roles") String roles,
268       @FormParam("users") String users
269   ) {
270     try {
271       jpaGroupRoleProvider.createGroup(name, description, roles, users);
272     } catch (IllegalArgumentException e) {
273       logger.warn("Unable to create group {}: {}", name, e.getMessage());
274       return Response.status(SC_BAD_REQUEST).build();
275     } catch (UnauthorizedException e) {
276       return Response.status(SC_FORBIDDEN).build();
277     } catch (ConflictException e) {
278       return Response.status(SC_CONFLICT).build();
279     }
280     return Response.status(SC_CREATED).build();
281   }
282 
283   @PUT
284   @Path("{id}")
285   @RestQuery(
286       name = "updateGroup",
287       description = "Update a group",
288       returnDescription = "Return the status codes",
289       pathParameters = {
290           @RestParameter(name = "id", description = "The group identifier", isRequired = true, type = Type.STRING),
291       },
292       restParameters = {
293           @RestParameter(
294               name = "name",
295               description = "The group name",
296               isRequired = true,
297               type = Type.STRING
298           ),
299           @RestParameter(
300               name = "description",
301               description = "The group description",
302               isRequired = false,
303               type = Type.STRING
304           ),
305           @RestParameter(
306               name = "roles",
307               description = "A comma seperated string of additional group roles",
308               isRequired = false,
309               type = Type.TEXT
310           ),
311           @RestParameter(
312               name = "users",
313               description = "A comma seperated string of group members",
314               isRequired = true,
315               type = Type.TEXT
316           ),
317       },
318       responses = {
319           @RestResponse(
320               responseCode = SC_OK,
321               description = "Group updated"
322           ),
323           @RestResponse(
324               responseCode = SC_FORBIDDEN,
325               description = "Not enough permissions to update a group with the admin role."
326           ),
327           @RestResponse(
328               responseCode = SC_NOT_FOUND,
329               description = "Group not found"
330           ),
331           @RestResponse(
332               responseCode = SC_BAD_REQUEST,
333               description = "Name too long"
334           ),
335       }
336   )
337   public Response updateGroup(
338       @PathParam("id") String groupId,
339       @FormParam("name") String name,
340       @FormParam("description") String description,
341       @FormParam("roles") String roles,
342       @FormParam("users") String users
343   ) throws NotFoundException {
344     try {
345       jpaGroupRoleProvider.updateGroup(groupId, name, description, roles, users);
346     } catch (IllegalArgumentException e) {
347       logger.warn("Unable to update group id {}: {}", groupId, e.getMessage());
348       return Response.status(SC_BAD_REQUEST).build();
349     } catch (UnauthorizedException ex) {
350       return Response.status(SC_FORBIDDEN).build();
351     }
352     return Response.ok().build();
353   }
354 
355   /**
356    * Borrowed from FileUploadRestService.java
357    *
358    * Builds an error message in case of an unexpected error in an endpoint
359    * method, includes the exception type and message if existing.
360    *
361    * TODO append stack trace
362    *
363    * @param e
364    *          Exception that was thrown
365    * @return error message
366    */
367   private String buildUnexpectedErrorMessage(Exception e) {
368     StringBuilder sb = new StringBuilder();
369     sb.append("Unexpected error (").append(e.getClass().getName()).append(")");
370     String message = e.getMessage();
371     if (StringUtils.isNotBlank(message)) {
372       sb.append(": ").append(message);
373     }
374     return sb.toString();
375   }
376 }