Skip to content

Commit 1755f48

Browse files
#5 Delete signatures (PDF)
1 parent bfcd151 commit 1755f48

File tree

3 files changed

+215
-60
lines changed

3 files changed

+215
-60
lines changed

esign-cert-repo/src/main/amp/config/alfresco/module/esign-cert-repo/context/service-context.xml

+14
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,19 @@
2424
<property name="versionService" ref="VersionService" />
2525
<property name="contentService" ref="ContentService" />
2626
</bean>
27+
28+
<bean id="deleteRelatedDocumentsBehaviour" class="es.keensoft.alfresco.behaviour.TransactionalDeleteBehaviour" init-method="init">
29+
<property name="policyComponent" ref="policyComponent"/>
30+
<property name="nodeService" ref="NodeService"/>
31+
<property name="transactionService" ref="TransactionService" />
32+
<property name="threadPoolExecutor" ref="relatedDocumentsThreadPoolExecutor" />
33+
</bean>
34+
35+
<bean id="relatedDocumentsThreadPoolExecutor" class="org.alfresco.util.ThreadPoolExecutorFactoryBean">
36+
<property name="poolName" value="relatedDocumentsThreadPool" />
37+
<property name="corePoolSize" value="1" />
38+
<property name="maximumPoolSize" value="5" />
39+
<property name="threadPriority" value="5" />
40+
</bean>
2741

2842
</beans>
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
package es.keensoft.alfresco.behaviour;
22

3-
import java.io.IOException;
43
import java.io.InputStream;
54
import java.io.Serializable;
65
import java.security.KeyStore;
7-
import java.security.KeyStoreException;
8-
import java.security.cert.CertificateException;
96
import java.security.cert.X509Certificate;
107
import java.util.ArrayList;
11-
import java.util.Calendar;
12-
import java.util.Date;
138
import java.util.HashMap;
149
import java.util.Map;
1510

@@ -28,30 +23,28 @@
2823
import org.alfresco.service.cmr.version.VersionService;
2924
import org.alfresco.service.namespace.QName;
3025
import org.apache.commons.io.FilenameUtils;
31-
import org.apache.commons.logging.Log;
32-
import org.apache.commons.logging.LogFactory;
3326

3427
import com.itextpdf.text.pdf.AcroFields;
3528
import com.itextpdf.text.pdf.PdfPKCS7;
3629
import com.itextpdf.text.pdf.PdfReader;
3730

3831
import es.keensoft.alfresco.model.SignModel;
3932

40-
public class CustomBehaviour implements NodeServicePolicies.OnDeleteAssociationPolicy, NodeServicePolicies.OnCreateNodePolicy {
33+
public class CustomBehaviour implements
34+
NodeServicePolicies.OnDeleteAssociationPolicy,
35+
NodeServicePolicies.OnCreateNodePolicy {
4136

4237
private PolicyComponent policyComponent;
4338
private NodeService nodeService;
4439
private VersionService versionService;
4540
private ContentService contentService;
4641

4742
private static final String PADES = "PAdES";
48-
private static Log log = LogFactory.getLog(CustomBehaviour.class);
4943

5044
public void init() {
5145
policyComponent.bindAssociationBehaviour(NodeServicePolicies.OnDeleteAssociationPolicy.QNAME,
5246
SignModel.ASPECT_SIGNATURE, new JavaBehaviour(this, "onDeleteAssociation",
5347
NotificationFrequency.TRANSACTION_COMMIT));
54-
log.debug("Enter create node");
5548
policyComponent.bindClassBehaviour(
5649
NodeServicePolicies.OnCreateNodePolicy.QNAME,
5750
ContentModel.PROP_CONTENT,
@@ -63,23 +56,19 @@ SignModel.ASPECT_SIGNATURE, new JavaBehaviour(this, "onDeleteAssociation",
6356
@Override
6457
public void onCreateNode(ChildAssociationRef childNodeRef) {
6558

66-
log.debug("Enter create node");
6759
NodeRef node = childNodeRef.getChildRef();
68-
ContentData contentData = (ContentData) nodeService.getProperty(node, ContentModel.PROP_CONTENT);
69-
//Do this check only if the uploaded document is a PDF
70-
if(contentData.getMimetype().equalsIgnoreCase("application/pdf")) {
71-
72-
log.debug("Is PDF");
73-
try {
60+
if (nodeService.exists(node)) {
61+
ContentData contentData = (ContentData) nodeService.getProperty(node, ContentModel.PROP_CONTENT);
62+
// Do this check only if the uploaded document is a PDF
63+
if (contentData.getMimetype().equalsIgnoreCase("application/pdf")) {
7464
ArrayList<Map<QName, Serializable>> signatures = getDigitalSignatures(node);
75-
//Add the aspect asociation
7665
if(signatures != null) {
7766
for(Map<QName, Serializable> aspectProperties : signatures) {
7867
String originalFileName = nodeService.getProperty(node, ContentModel.PROP_NAME).toString();
7968
String signatureFileName = FilenameUtils.getBaseName(originalFileName) + "-"
8069
+ System.currentTimeMillis() + "-" + PADES;
8170

82-
// Creating a node reference without type (no content and no folder), remains invisible for Share
71+
// Creating a node reference without type (no content and no folder): remains invisible for Share
8372
NodeRef signatureNodeRef = nodeService.createNode(
8473
nodeService.getPrimaryParent(node).getParentRef(),
8574
ContentModel.ASSOC_CONTAINS,
@@ -94,62 +83,51 @@ public void onCreateNode(ChildAssociationRef childNodeRef) {
9483
}
9584
}
9685
}
97-
catch(Exception ex) {
98-
log.error(ex.toString());
99-
}
10086
}
10187
}
10288

10389
@Override
10490
public void onDeleteAssociation(AssociationRef nodeAssocRef) {
105-
// Delete SIGNED aspect on SIGNATURE deletion
10691
if (nodeService.exists(nodeAssocRef.getTargetRef())) {
10792
nodeService.removeAspect(nodeAssocRef.getTargetRef(), SignModel.ASPECT_SIGNED);
10893
}
10994
}
11095

111-
public ArrayList<Map<QName, Serializable>> getDigitalSignatures(NodeRef node) throws IOException, KeyStoreException, Exception, CertificateException {
96+
public ArrayList<Map<QName, Serializable>> getDigitalSignatures(NodeRef node) {
11297

113-
ContentReader contentReader = contentService.getReader(node, ContentModel.PROP_CONTENT);
114-
InputStream is = contentReader.getContentInputStream();
98+
try {
11599

116-
PdfReader reader = new PdfReader(is);
117-
AcroFields af = reader.getAcroFields();
118-
ArrayList<String> names = af.getSignatureNames();
119-
if(names == null || names.isEmpty()) return null;
120-
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
121-
ks.load(null, null);
122-
ArrayList<Map<QName, Serializable>> aspects = new ArrayList<Map<QName, Serializable>>();
123-
for (String name : names) {
124-
PdfPKCS7 pk = af.verifySignature(name);
125-
X509Certificate certificate = pk.getSigningCertificate();
126-
127-
//Set aspect properties for each signature
128-
Map<QName, Serializable> aspectSignatureProperties = new HashMap<QName, Serializable>();
129-
aspectSignatureProperties.put(SignModel.PROP_DATE, convertCalendarToDate(pk.getSignDate()));
130-
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_PRINCIPAL, certificate.getSubjectX500Principal().toString());
131-
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_SERIAL_NUMBER, certificate.getSerialNumber().toString());
132-
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_NOT_AFTER, certificate.getNotAfter());
133-
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_ISSUER, certificate.getIssuerX500Principal().toString());
134-
aspects.add(aspectSignatureProperties);
135-
}
136-
return aspects;
100+
ContentReader contentReader = contentService.getReader(node, ContentModel.PROP_CONTENT);
101+
InputStream is = contentReader.getContentInputStream();
102+
103+
PdfReader reader = new PdfReader(is);
104+
AcroFields af = reader.getAcroFields();
105+
ArrayList<String> names = af.getSignatureNames();
106+
if(names == null || names.isEmpty()) return null;
107+
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
108+
ks.load(null, null);
109+
ArrayList<Map<QName, Serializable>> aspects = new ArrayList<Map<QName, Serializable>>();
110+
for (String name : names) {
111+
PdfPKCS7 pk = af.verifySignature(name);
112+
X509Certificate certificate = pk.getSigningCertificate();
113+
114+
//Set aspect properties for each signature
115+
Map<QName, Serializable> aspectSignatureProperties = new HashMap<QName, Serializable>();
116+
aspectSignatureProperties.put(SignModel.PROP_DATE, pk.getSignDate().getTime());
117+
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_PRINCIPAL, certificate.getSubjectX500Principal().toString());
118+
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_SERIAL_NUMBER, certificate.getSerialNumber().toString());
119+
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_NOT_AFTER, certificate.getNotAfter());
120+
aspectSignatureProperties.put(SignModel.PROP_CERTIFICATE_ISSUER, certificate.getIssuerX500Principal().toString());
121+
aspects.add(aspectSignatureProperties);
122+
}
123+
return aspects;
124+
125+
} catch (Exception e) {
126+
throw new RuntimeException(e);
127+
}
137128
}
138129

139130

140-
@SuppressWarnings({"deprecation" })
141-
private Date convertCalendarToDate(Calendar cal) {
142-
Date date = new Date();
143-
date.setDate(cal.get(Calendar.DATE));
144-
date.setMonth(cal.get(Calendar.MONTH));
145-
date.setYear(cal.get(Calendar.YEAR) - 1900);
146-
date.setHours(cal.get(Calendar.HOUR));
147-
date.setMinutes(cal.get(Calendar.MINUTE));
148-
date.setSeconds(cal.get(Calendar.SECOND));
149-
System.out.println(date);
150-
return date;
151-
}
152-
153131
public PolicyComponent getPolicyComponent() {
154132
return policyComponent;
155133
}
@@ -181,4 +159,6 @@ public ContentService getContentService() {
181159
public void setContentService(ContentService contentService) {
182160
this.contentService = contentService;
183161
}
162+
163+
184164
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
package es.keensoft.alfresco.behaviour;
2+
3+
import java.util.ArrayList;
4+
import java.util.List;
5+
import java.util.concurrent.ThreadPoolExecutor;
6+
7+
import org.alfresco.repo.node.NodeServicePolicies;
8+
import org.alfresco.repo.policy.Behaviour;
9+
import org.alfresco.repo.policy.JavaBehaviour;
10+
import org.alfresco.repo.policy.PolicyComponent;
11+
import org.alfresco.repo.security.authentication.AuthenticationUtil;
12+
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
13+
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
14+
import org.alfresco.repo.transaction.RetryingTransactionHelper;
15+
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
16+
import org.alfresco.repo.transaction.TransactionListener;
17+
import org.alfresco.service.cmr.repository.AssociationRef;
18+
import org.alfresco.service.cmr.repository.NodeRef;
19+
import org.alfresco.service.cmr.repository.NodeService;
20+
import org.alfresco.service.transaction.TransactionService;
21+
import org.alfresco.util.transaction.TransactionListenerAdapter;
22+
23+
import es.keensoft.alfresco.model.SignModel;
24+
25+
public class TransactionalDeleteBehaviour implements NodeServicePolicies.BeforeDeleteNodePolicy {
26+
27+
// Key to identify resources associated to transaction
28+
private static final String KEY_RELATED_NODES = TransactionalDeleteBehaviour.class.getName() + ".relatedNodes";
29+
30+
private PolicyComponent policyComponent;
31+
private NodeService nodeService;
32+
private TransactionService transactionService;
33+
private TransactionListener transactionListener;
34+
private ThreadPoolExecutor threadPoolExecutor;
35+
36+
// Bind behaviour and initialize transaction listener
37+
public void init() {
38+
39+
policyComponent.bindClassBehaviour(
40+
NodeServicePolicies.BeforeDeleteNodePolicy.QNAME,
41+
SignModel.ASPECT_SIGNED,
42+
new JavaBehaviour(this, "beforeDeleteNode", Behaviour.NotificationFrequency.FIRST_EVENT));
43+
44+
this.transactionListener = new RelatedNodesTransactionListener();
45+
46+
}
47+
48+
49+
@Override
50+
public void beforeDeleteNode(NodeRef nodeRef) {
51+
52+
// Bind listener to current transaction
53+
AlfrescoTransactionSupport.bindListener(transactionListener);
54+
55+
// Get some related nodes to work with
56+
List<NodeRef> relatedNodes = new ArrayList<NodeRef>();
57+
for (AssociationRef signatureAssoc : nodeService.getTargetAssocs(nodeRef, SignModel.ASSOC_SIGNATURE)) {
58+
relatedNodes.add(signatureAssoc.getTargetRef());
59+
}
60+
61+
// Transactions involving several nodes need resource updating
62+
List<NodeRef> currentRelatedNodes = AlfrescoTransactionSupport.getResource(KEY_RELATED_NODES);
63+
if (currentRelatedNodes == null) {
64+
currentRelatedNodes = relatedNodes;
65+
} else {
66+
currentRelatedNodes.addAll(relatedNodes);
67+
}
68+
69+
// Put resources to be used in transaction listener
70+
AlfrescoTransactionSupport.bindResource(KEY_RELATED_NODES, currentRelatedNodes);
71+
72+
}
73+
74+
// Listening "afterCommit" transaction event
75+
private class RelatedNodesTransactionListener
76+
extends TransactionListenerAdapter implements TransactionListener {
77+
78+
@Override
79+
public void afterCommit() {
80+
@SuppressWarnings("unchecked")
81+
List<NodeRef> nodesToBeReviewed =
82+
(List<NodeRef>) AlfrescoTransactionSupport.getResource(KEY_RELATED_NODES);
83+
if (nodesToBeReviewed != null) {
84+
for (NodeRef nodeToBeReviewed : nodesToBeReviewed) {
85+
// Launch every node work in a different thread
86+
Runnable runnable = new RelatedNodeDeletion(nodeToBeReviewed);
87+
threadPoolExecutor.execute(runnable);
88+
}
89+
}
90+
}
91+
92+
@Override
93+
public void flush() {
94+
}
95+
96+
}
97+
98+
// Thread to work with an individual node
99+
private class RelatedNodeDeletion implements Runnable {
100+
101+
private NodeRef nodeToBeReviewed;
102+
103+
private RelatedNodeDeletion(NodeRef nodeToBeReviewed) {
104+
this.nodeToBeReviewed = nodeToBeReviewed;
105+
}
106+
107+
@Override
108+
public void run() {
109+
AuthenticationUtil.runAsSystem(new RunAsWork<Void>() {
110+
111+
public Void doWork() throws Exception {
112+
113+
RetryingTransactionCallback<Void> callback = new RetryingTransactionCallback<Void>() {
114+
115+
@Override
116+
public Void execute() throws Throwable {
117+
nodeService.deleteNode(nodeToBeReviewed);
118+
return null;
119+
}
120+
};
121+
122+
try {
123+
RetryingTransactionHelper txnHelper =
124+
transactionService.getRetryingTransactionHelper();
125+
txnHelper.doInTransaction(callback, false, true);
126+
} catch (Throwable e) {
127+
e.printStackTrace();
128+
}
129+
130+
return null;
131+
132+
}
133+
});
134+
}
135+
136+
}
137+
138+
public void setPolicyComponent(PolicyComponent policyComponent) {
139+
this.policyComponent = policyComponent;
140+
}
141+
142+
143+
public void setNodeService(NodeService nodeService) {
144+
this.nodeService = nodeService;
145+
}
146+
147+
148+
public void setTransactionService(TransactionService transactionService) {
149+
this.transactionService = transactionService;
150+
}
151+
152+
153+
public void setTransactionListener(TransactionListener transactionListener) {
154+
this.transactionListener = transactionListener;
155+
}
156+
157+
158+
public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
159+
this.threadPoolExecutor = threadPoolExecutor;
160+
}
161+
}

0 commit comments

Comments
 (0)