001/* 002 * (C) Copyright 2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Thomas Roger 018 */ 019 020package org.nuxeo.ecm.permissions; 021 022import static org.nuxeo.ecm.permissions.Constants.ACE_GRANTED_TEMPLATE; 023import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_COMMENT; 024import static org.nuxeo.ecm.permissions.Constants.ACE_INFO_DIRECTORY; 025import static org.nuxeo.ecm.permissions.Constants.ACE_KEY; 026import static org.nuxeo.ecm.permissions.Constants.ACL_NAME_KEY; 027import static org.nuxeo.ecm.permissions.Constants.PERMISSION_NOTIFICATION_EVENT; 028 029import java.util.Collections; 030 031import org.apache.commons.lang.StringEscapeUtils; 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.nuxeo.ecm.automation.AutomationService; 035import org.nuxeo.ecm.automation.OperationChain; 036import org.nuxeo.ecm.automation.OperationContext; 037import org.nuxeo.ecm.automation.OperationException; 038import org.nuxeo.ecm.automation.core.operations.notification.SendMail; 039import org.nuxeo.ecm.automation.core.scripting.Expression; 040import org.nuxeo.ecm.automation.core.scripting.Scripting; 041import org.nuxeo.ecm.automation.core.util.StringList; 042import org.nuxeo.ecm.automation.features.PlatformFunctions; 043import org.nuxeo.ecm.core.api.CoreSession; 044import org.nuxeo.ecm.core.api.DocumentModel; 045import org.nuxeo.ecm.core.api.NuxeoGroup; 046import org.nuxeo.ecm.core.api.NuxeoPrincipal; 047import org.nuxeo.ecm.core.api.security.ACE; 048import org.nuxeo.ecm.core.event.Event; 049import org.nuxeo.ecm.core.event.EventBundle; 050import org.nuxeo.ecm.core.event.EventContext; 051import org.nuxeo.ecm.core.event.PostCommitFilteringEventListener; 052import org.nuxeo.ecm.core.event.impl.DocumentEventContext; 053import org.nuxeo.ecm.directory.Session; 054import org.nuxeo.ecm.directory.api.DirectoryService; 055import org.nuxeo.ecm.platform.ec.notification.service.NotificationService; 056import org.nuxeo.ecm.platform.ec.notification.service.NotificationServiceHelper; 057import org.nuxeo.ecm.platform.ui.web.tag.fn.Functions; 058import org.nuxeo.ecm.platform.usermanager.UserManager; 059import org.nuxeo.ecm.tokenauth.service.TokenAuthenticationService; 060import org.nuxeo.runtime.api.Framework; 061 062/** 063 * Listener sending an email notification for a granted ACE. 064 * <p> 065 * This listener checks only if the ACE is granted. It assumes that other checks (such as the ACE becomes effective) 066 * have been done before. 067 * 068 * @since 7.4 069 */ 070public class PermissionGrantedNotificationListener implements PostCommitFilteringEventListener { 071 072 private static final Log log = LogFactory.getLog(PermissionGrantedNotificationListener.class); 073 074 public static final String SUBJECT_FORMAT = "%s %s %s"; 075 076 @Override 077 public void handleEvent(EventBundle events) { 078 for (Event event : events) { 079 handleEvent(event); 080 } 081 } 082 083 protected void handleEvent(Event event) { 084 EventContext eventCtx = event.getContext(); 085 if (!(eventCtx instanceof DocumentEventContext)) { 086 return; 087 } 088 089 DocumentEventContext docCtx = (DocumentEventContext) eventCtx; 090 CoreSession coreSession = docCtx.getCoreSession(); 091 DocumentModel doc = docCtx.getSourceDocument(); 092 if (doc == null || !coreSession.exists(doc.getRef())) { 093 return; 094 } 095 096 ACE ace = (ACE) docCtx.getProperty(ACE_KEY); 097 String aclName = (String) docCtx.getProperty(ACL_NAME_KEY); 098 if (ace == null || ace.isDenied() || aclName == null) { 099 return; 100 } 101 102 String username = ace.getUsername(); 103 StringList to = getRecipients(username); 104 if (to == null) { 105 // no recipient 106 return; 107 } 108 109 Expression from = Scripting.newExpression("Env[\"mail.from\"]"); 110 NotificationService notificationService = NotificationServiceHelper.getNotificationService(); 111 String subject = String.format(SUBJECT_FORMAT, notificationService.getEMailSubjectPrefix(), 112 "New permission on", doc.getTitle()); 113 114 DirectoryService directoryService = Framework.getService(DirectoryService.class); 115 try (Session session = directoryService.open(ACE_INFO_DIRECTORY)) { 116 String id = PermissionHelper.computeDirectoryId(doc, aclName, ace.getId()); 117 DocumentModel entry = session.getEntry(id); 118 119 OperationContext ctx = new OperationContext(coreSession); 120 ctx.setInput(doc); 121 ctx.put("ace", ace); 122 if (entry != null) { 123 String comment = (String) entry.getPropertyValue(ACE_INFO_COMMENT); 124 if (comment != null) { 125 comment = StringEscapeUtils.escapeHtml(comment); 126 comment = comment.replaceAll("\n", "<br/>"); 127 ctx.put("comment", comment); 128 } 129 } 130 String aceCreator = ace.getCreator(); 131 if (aceCreator != null) { 132 UserManager userManager = Framework.getService(UserManager.class); 133 NuxeoPrincipal creator = userManager.getPrincipal(aceCreator); 134 if (creator != null) { 135 ctx.put("aceCreator", 136 String.format("%s (%s)", Functions.principalFullName(creator), creator.getName())); 137 } 138 } 139 140 if (NuxeoPrincipal.isTransientUsername(username)) { 141 TokenAuthenticationService tokenAuthenticationService = Framework.getService(TokenAuthenticationService.class); 142 String token = tokenAuthenticationService.getToken(username, doc.getRepositoryName(), doc.getId()); 143 if (token != null) { 144 ctx.put("token", token); 145 } 146 } 147 148 OperationChain chain = new OperationChain("SendMail"); 149 chain.add(SendMail.ID) 150 .set("from", from) 151 .set("to", to) 152 .set("HTML", true) 153 .set("subject", subject) 154 .set("message", ACE_GRANTED_TEMPLATE); 155 Framework.getService(AutomationService.class).run(ctx, chain); 156 } catch (OperationException e) { 157 log.warn("Unable to notify user", e); 158 log.debug(e, e); 159 } 160 } 161 162 protected StringList getRecipients(String username) { 163 UserManager userManager = Framework.getService(UserManager.class); 164 NuxeoPrincipal principal = userManager.getPrincipal(username); 165 StringList to = null; 166 if (principal != null) { 167 to = new StringList(Collections.singletonList(principal.getEmail())); 168 } else { 169 NuxeoGroup group = userManager.getGroup(username); 170 if (group != null) { 171 PlatformFunctions platformFunctions = new PlatformFunctions(); 172 to = platformFunctions.getEmailsFromGroup(group.getName()); 173 } 174 } 175 return to; 176 } 177 178 @Override 179 public boolean acceptEvent(Event event) { 180 String eventName = event.getName(); 181 return PERMISSION_NOTIFICATION_EVENT.equals(eventName); 182 } 183}