View Javadoc
1   /*
2    * Copyright (C) 2003-2019 eXo Platform SAS.
3    *
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU Affero General Public License as published by
6    * the Free Software Foundation, either version 3 of the License, or
7    * (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU Affero General Public License for more details.
13   *
14   * You should have received a copy of the GNU Affero General Public License
15   * along with this program. If not, see <http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.social.service.rest;
18  
19  import java.util.Arrays;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.annotation.security.RolesAllowed;
25  import javax.ws.rs.GET;
26  import javax.ws.rs.Path;
27  import javax.ws.rs.PathParam;
28  import javax.ws.rs.WebApplicationException;
29  import javax.ws.rs.core.Context;
30  import javax.ws.rs.core.MediaType;
31  import javax.ws.rs.core.Response;
32  import javax.ws.rs.core.UriInfo;
33  
34  import static org.exoplatform.social.service.rest.RestChecker.checkAuthenticatedUserPermission;
35  import org.exoplatform.commons.api.notification.NotificationContext;
36  import org.exoplatform.commons.api.notification.NotificationMessageUtils;
37  import org.exoplatform.commons.api.notification.channel.AbstractChannel;
38  import org.exoplatform.commons.api.notification.channel.template.AbstractTemplateBuilder;
39  import org.exoplatform.commons.api.notification.model.ChannelKey;
40  import org.exoplatform.commons.api.notification.model.MessageInfo;
41  import org.exoplatform.commons.api.notification.model.NotificationInfo;
42  import org.exoplatform.commons.api.notification.model.PluginKey;
43  import org.exoplatform.commons.api.notification.model.WebNotificationFilter;
44  import org.exoplatform.commons.api.notification.plugin.BaseNotificationPlugin;
45  import org.exoplatform.commons.api.notification.service.storage.WebNotificationStorage;
46  import org.exoplatform.web.security.csrf.ExoCSRFCheck;
47  import org.exoplatform.commons.notification.channel.WebChannel;
48  import org.exoplatform.commons.notification.impl.NotificationContextImpl;
49  import org.exoplatform.commons.notification.net.WebNotificationSender;
50  import org.exoplatform.services.log.ExoLogger;
51  import org.exoplatform.services.log.Log;
52  import org.exoplatform.services.rest.resource.ResourceContainer;
53  import org.exoplatform.social.core.identity.model.Identity;
54  import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
55  import org.exoplatform.social.core.manager.IdentityManager;
56  import org.exoplatform.social.core.manager.RelationshipManager;
57  import org.exoplatform.social.core.relationship.model.Relationship;
58  import org.exoplatform.social.core.space.model.Space;
59  import org.exoplatform.social.core.space.spi.SpaceService;
60  import org.exoplatform.social.core.storage.impl.AbstractStorage;
61  
62  /**
63   * Created by The eXo Platform SAS
64   * Author : eXoPlatform
65   *          exo@exoplatform.com
66   * Nov 26, 2014  
67   */
68  @Path("social/intranet-notification")
69  public class IntranetNotificationRestService extends AbstractStorage implements ResourceContainer {
70  
71    private static final Log LOG = ExoLogger.getLogger(IntranetNotificationRestService.class);
72  
73    private IdentityManager identityManager;
74    private RelationshipManager relationshipManager;
75    private SpaceService spaceService;
76    private WebNotificationStorage webNotificationStorage;
77  
78    public final static String MESSAGE_JSON_FILE_NAME = "message.json";
79  
80    public IntranetNotificationRestService(IdentityManager identityManager, RelationshipManager relationshipManager,
81                                           SpaceService spaceService, WebNotificationStorage webNotificationStorage) {
82      this.identityManager = identityManager;
83      this.relationshipManager = relationshipManager;
84      this.spaceService = spaceService;
85      this.webNotificationStorage = webNotificationStorage;
86    }
87  
88    /**
89     * Processes the "Accept the invitation to connect" action between 2 users and update notification.
90     *
91     * @param senderId The remote Id of the identity who sent the invitation.
92     * @param receiverId The remote Id of the identity who received the invitation.
93     * @notificationId Id of the web notification message
94     * @authentication
95     * @request
96     * GET: {@code http://localhost:8080/rest/social/intranet-notifications/confirmInvitationToConnect/john/root/<notificationId>/message.json}
97     * @throws Exception
98     */
99    @GET
100   @RolesAllowed("users")
101   @ExoCSRFCheck
102   @Path("confirmInvitationToConnect/{senderId}/{receiverId}/{notificationId}/message.{format}")
103   public Response confirmInvitationToConnect(@Context UriInfo uriInfo,
104                                              @PathParam("senderId") String senderId,
105                                              @PathParam("receiverId") String receiverId,
106                                              @PathParam("notificationId") String notificationId,
107                                              @PathParam("format") String format) throws Exception {
108     //Check authenticated user
109     checkAuthenticatedUserPermission(receiverId);
110 
111     Identity sender = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, senderId, true);
112     Identity receiver = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, receiverId, true);
113     if (sender == null || receiver == null) {
114       throw new WebApplicationException(Response.Status.BAD_REQUEST);
115     }
116 
117     Relationship invitation = relationshipManager.get(sender, receiver);
118     if(invitation == null || !invitation.getStatus().equals(Relationship.Type.PENDING) || !invitation.isReceiver(receiver)) {
119       throw new WebApplicationException(Response.Status.FORBIDDEN);
120     }
121 
122     //
123     String[] mediaTypes = new String[] { "json", "xml" };
124     MediaType mediaType = Util.getMediaType(format, mediaTypes);
125     //update notification
126     NotificationInfo info = webNotificationStorage.get(notificationId);
127     info.key(new PluginKey("RelationshipReceivedRequestPlugin"));
128     info.setFrom(senderId);
129     info.setTo(receiverId);
130     Map<String, String> ownerParameter = new HashMap<>();
131     ownerParameter.put("sender", senderId);
132     ownerParameter.put("status", "accepted");
133     info.setOwnerParameter(ownerParameter);
134     MessageInfo messageInfo = sendBackNotif(info);
135     if (messageInfo == null) {
136       throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
137     }
138 
139     relationshipManager.confirm(sender, receiver);
140 
141     return Util.getResponse(messageInfo, uriInfo, mediaType, Response.Status.OK);
142   }
143   /**
144    * Processes the "Deny the invitation to connect" action between 2 users
145    *
146    * @param senderId The sender's remote Id.
147    * @param receiverId The receiver's remote Id.
148    * @authentication
149    * @request
150    * GET: localhost:8080/rest/social/intranet-notifications/ignoreInvitationToConnect/john/root
151    * @throws Exception
152    */
153   @GET
154   @RolesAllowed("users")
155   @ExoCSRFCheck
156   @Path("ignoreInvitationToConnect/{senderId}/{receiverId}/{notificationId}/message.{format}")
157   public Response ignoreInvitationToConnect(@Context UriInfo uriInfo,
158                                           @PathParam("senderId") String senderId,
159                                           @PathParam("receiverId") String receiverId,
160                                           @PathParam("notificationId") String notificationId,
161                                           @PathParam("format") String format) throws Exception {
162     //Check authenticated user
163     checkAuthenticatedUserPermission(receiverId);
164     //
165     String[] mediaTypes = new String[] { "json", "xml" };
166     MediaType mediaType = Util.getMediaType(format, mediaTypes);
167     //
168     Identity sender = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, senderId, true);
169     Identity receiver = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, receiverId, true);
170     if (sender == null || receiver == null) {
171       throw new WebApplicationException(Response.Status.BAD_REQUEST);
172     }
173     relationshipManager.deny(sender, receiver);
174     webNotificationStorage.remove(notificationId);
175     //
176     return Util.getResponse(getUserWebNotification(receiverId), uriInfo, mediaType, Response.Status.OK);
177   }
178 
179   /**
180    * Processes the "Accept the invitation to join a space" action and update notification.
181    *
182    * @param userId The invitee's remote Id.
183    * @param spaceId Id of the space.
184    * @notificationId of the web notification message
185    * @authentication
186    * @request
187    * GET: {@code localhost:8080/rest/social/intranet-notifications/acceptInvitationToJoinSpace/e1cacf067f0001015ac312536462fc6b/john/<notificationId>/message.json}
188    * @throws Exception
189    */
190   @GET
191   @RolesAllowed("users")
192   @ExoCSRFCheck
193   @Path("acceptInvitationToJoinSpace/{spaceId}/{userId}/{notificationId}/message.{format}")
194   public Response acceptInvitationToJoinSpace(@Context UriInfo uriInfo,
195                                               @PathParam("spaceId") String spaceId,
196                                                @PathParam("userId") String userId,
197                                                @PathParam("notificationId") String notificationId,
198                                                @PathParam("format") String format) throws Exception {
199     //Check authenticated user
200     checkAuthenticatedUserPermission(userId);
201     //
202     Space space = spaceService.getSpaceById(spaceId);
203     if (space == null) {
204       throw new WebApplicationException(Response.Status.BAD_REQUEST);
205     }
206 
207     List<String> invitedUsers = Arrays.asList(space.getInvitedUsers());
208     if (!invitedUsers.contains(userId)) {
209       throw new WebApplicationException(Response.Status.FORBIDDEN);
210     }
211     //
212     String[] mediaTypes = new String[] { "json", "xml" };
213     MediaType mediaType = Util.getMediaType(format, mediaTypes);
214 
215     //update notification
216     NotificationInfo info = webNotificationStorage.get(notificationId);
217     info.setTo(userId);
218     info.key(new PluginKey("SpaceInvitationPlugin"));
219     Map<String, String> ownerParameter = new HashMap<String, String>();
220     ownerParameter.put("spaceId", spaceId);
221     ownerParameter.put("status", "accepted");
222     info.setOwnerParameter(ownerParameter);
223     MessageInfo messageInfo = sendBackNotif(info);
224     if (messageInfo == null) {
225       throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
226     }
227 
228     spaceService.addMember(space, userId);
229     return Util.getResponse(messageInfo, uriInfo, mediaType, Response.Status.OK);
230   }
231   
232   /**
233    * Processes the "Deny the invitation to join a space" action.
234    *
235    * @param userId The invitee's remote Id.
236    * @param spaceId Id of the space.
237    * @authentication
238    * @request
239    * GET: localhost:8080/rest/social/intranet-notifications/ignoreInvitationToJoinSpace/e1cacf067f0001015ac312536462fc6b/john
240    * @throws Exception
241    */
242   @GET
243   @RolesAllowed("users")
244   @ExoCSRFCheck
245   @Path("ignoreInvitationToJoinSpace/{spaceId}/{userId}/{notificationId}/message.{format}")
246   public Response ignoreInvitationToJoinSpace(@Context UriInfo uriInfo,
247                                            @PathParam("spaceId") String spaceId,
248                                            @PathParam("userId") String userId,
249                                            @PathParam("notificationId") String notificationId,
250                                            @PathParam("format") String format) throws Exception {
251     //Check authenticated user
252     checkAuthenticatedUserPermission(userId);
253     //
254     String[] mediaTypes = new String[] { "json", "xml" };
255     MediaType mediaType = Util.getMediaType(format, mediaTypes);
256     //
257     Space space = spaceService.getSpaceById(spaceId);
258     if (space == null) {
259       throw new WebApplicationException(Response.Status.BAD_REQUEST);
260     }
261     spaceService.removeInvitedUser(space, userId);
262     //
263     webNotificationStorage.remove(notificationId);
264     
265     return Util.getResponse(getUserWebNotification(userId), uriInfo, mediaType, Response.Status.OK);
266   }
267   
268   /**
269    * Adds a member to a space and update notification.
270    *
271    * @param uriInfo
272    * @param spaceId Id of the space.
273    * @param requestUserId The remote Id of the user who requests for joining the space.
274    * @param currentUserId the userId
275    * @param notificationId
276    * @param format
277    * @notificationId of the web notification message
278    * @authentication
279    * @request
280    * GET: {@code localhost:8080/rest/social/intranet-notifications/validateRequestToJoinSpace/e1cacf067f0001015ac312536462fc6b/john/<notificationId>/message.json}
281    * @throws Exception
282    */
283   @GET
284   @RolesAllowed("users")
285   @ExoCSRFCheck
286   @Path("validateRequestToJoinSpace/{spaceId}/{requestUserId}/{currentUserId}/{notificationId}/message.{format}")
287   public Response validateRequestToJoinSpace(@Context UriInfo uriInfo,
288                                          @PathParam("spaceId") String spaceId,
289                                          @PathParam("requestUserId") String requestUserId,
290                                          @PathParam("currentUserId") String currentUserId,
291                                          @PathParam("notificationId") String notificationId,
292                                          @PathParam("format") String format) throws Exception {
293     //Check authenticated user
294     checkAuthenticatedUserPermission(currentUserId);
295 
296     //check space existence
297     Space space = spaceService.getSpaceById(spaceId);
298     if (space == null) {
299       throw new WebApplicationException(Response.Status.BAD_REQUEST);
300     }
301 
302     //check that caller is manager
303     List<String> managers = Arrays.asList(space.getManagers());
304     if (!managers.contains(currentUserId)) {
305       throw new WebApplicationException(Response.Status.UNAUTHORIZED);
306     }
307 
308     //check that requestUserId is in the pending user list
309     List<String> pendingUsers = Arrays.asList(space.getPendingUsers());
310     if (!pendingUsers.contains(requestUserId)) {
311       throw new WebApplicationException(Response.Status.FORBIDDEN);
312     }
313 
314     //
315     String[] mediaTypes = new String[] { "json", "xml" };
316     MediaType mediaType = Util.getMediaType(format, mediaTypes);
317     //update notification
318     NotificationInfo info = webNotificationStorage.get(notificationId);
319     info.setTo(currentUserId);
320     info.key(new PluginKey("RequestJoinSpacePlugin"));
321     Map<String, String> ownerParameter = new HashMap<String, String>();
322     ownerParameter.put("spaceId", spaceId);
323     ownerParameter.put("request_from", requestUserId);
324     ownerParameter.put("status", "accepted");
325     info.setOwnerParameter(ownerParameter);
326     MessageInfo messageInfo = sendBackNotif(info);
327     if (messageInfo == null) {
328       throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
329     }
330 
331 
332     spaceService.addMember(space, requestUserId);
333     return Util.getResponse(messageInfo, uriInfo, mediaType, Response.Status.OK);
334   }
335   
336   /**
337    * Refuses a user's request for joining a space. 
338    *
339    * @param uriInfo
340    * @param spaceId Id of the space.
341    * @param requestUserId The remote Id of the user who requests for joining the space.
342    * @param currentUserId
343    * @param notificationId
344    * @param format
345    * @authentication
346    * @request
347    * GET: localhost:8080/rest/social/intranet-notifications/refuseRequestToJoinSpace/e1cacf067f0001015ac312536462fc6b/john
348    * @throws Exception
349    */
350   @GET
351   @RolesAllowed("users")
352   @ExoCSRFCheck
353   @Path("refuseRequestToJoinSpace/{spaceId}/{requestUserId}/{currentUserId}/{notificationId}/message.{format}")
354   public Response refuseRequestToJoinSpace(@Context UriInfo uriInfo,
355                                         @PathParam("spaceId") String spaceId,
356                                         @PathParam("requestUserId") String requestUserId,
357                                         @PathParam("currentUserId") String currentUserId,
358                                         @PathParam("notificationId") String notificationId,
359                                         @PathParam("format") String format) throws Exception {
360     //Check authenticated user
361     checkAuthenticatedUserPermission(currentUserId);
362     //
363     String[] mediaTypes = new String[] { "json", "xml" };
364     MediaType mediaType = Util.getMediaType(format, mediaTypes);
365     //
366     Space space = spaceService.getSpaceById(spaceId);
367     if (space == null) {
368       throw new WebApplicationException(Response.Status.BAD_REQUEST);
369     }
370     spaceService.removePendingUser(space, requestUserId);
371     webNotificationStorage.remove(notificationId);
372     //
373     return Util.getResponse(getUserWebNotification(currentUserId), uriInfo, mediaType, Response.Status.OK);
374   }
375   
376   private MessageInfo sendBackNotif(NotificationInfo notification) {
377     NotificationContext nCtx = NotificationContextImpl.cloneInstance().setNotificationInfo(notification);
378     BaseNotificationPlugin plugin = nCtx.getPluginContainer().getPlugin(notification.getKey());
379     if (plugin == null) {
380       return null;
381     }
382     try {
383       AbstractChannel channel = nCtx.getChannelManager().getChannel(ChannelKey.key(WebChannel.ID));
384       AbstractTemplateBuilder builder = channel.getTemplateBuilder(notification.getKey());
385       MessageInfo msg = builder.buildMessage(nCtx);
386       msg.setMoveTop(false);
387       WebNotificationSender.sendJsonMessage(notification.getTo(), msg);
388       notification.setTitle(msg.getBody());
389       notification.with(NotificationMessageUtils.SHOW_POPOVER_PROPERTY.getKey(), "true")
390                   .with(NotificationMessageUtils.READ_PORPERTY.getKey(), "false");
391       webNotificationStorage.update(notification, false);
392       return msg;
393     } catch (Exception e) {
394       LOG.error("Can not send the message to Intranet.", e.getMessage());
395       return null;
396     }
397   }
398 
399   private Map<String, Boolean> getUserWebNotification(String userId) throws Exception {
400     Map<String, Boolean> data = new HashMap<String, Boolean>();
401     List<NotificationInfo> notifications = webNotificationStorage.get(new WebNotificationFilter(userId), 0, 1);
402     data.put("showViewAll", (notifications.size() > 0));
403     return data;
404   }
405 
406 }