View Javadoc

1   /*
2    * #%L
3    * Bitrepository Protocol
4    * %%
5    * Copyright (C) 2010 - 2012 The State and University Library, The Royal Library and The State Archives, Denmark
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as 
9    * published by the Free Software Foundation, either version 2.1 of the 
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public 
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-2.1.html>.
20   * #L%
21   */
22  package org.bitrepository.protocol.security;
23  
24  import java.io.ByteArrayInputStream;
25  import java.io.IOException;
26  import java.security.Provider;
27  import java.security.Security;
28  import java.security.cert.CertificateException;
29  import java.security.cert.CertificateFactory;
30  import java.security.cert.X509Certificate;
31  import java.util.Collection;
32  import java.util.HashMap;
33  import java.util.HashSet;
34  import java.util.Map;
35  import java.util.Set;
36  
37  import org.bitrepository.protocol.security.exception.PermissionStoreException;
38  import org.bitrepository.settings.repositorysettings.InfrastructurePermission;
39  import org.bitrepository.settings.repositorysettings.Operation;
40  import org.bitrepository.settings.repositorysettings.OperationPermission;
41  import org.bitrepository.settings.repositorysettings.PermissionSet;
42  import org.bitrepository.settings.repositorysettings.Permission;
43  import org.bouncycastle.cms.SignerId;
44  import org.bouncycastle.jce.provider.BouncyCastleProvider;
45  import org.slf4j.Logger;
46  import org.slf4j.LoggerFactory;
47  
48  /**
49   * Class to hold the concept of permissions used in the Bitrepository.
50   * The class contains functionality to:
51   * - Hold the correlation between certificates and the permissions related to them.
52   * - Test if a certificate has a requested permission
53   * - Retreive a certificate from the store.
54   */
55  public class PermissionStore {
56  
57      private final Logger log = LoggerFactory.getLogger(PermissionStore.class);
58      /** Mapping from certificate identifier to an object containing the certificate and the permissions registered with it*/
59      private Map<CertificateID, CertificatePermission> permissionMap;
60  
61      /**
62       * Public constructor, initializes the store. 
63       */
64      public PermissionStore() {
65          permissionMap = new HashMap<CertificateID, CertificatePermission>();
66          Provider provider = new BouncyCastleProvider();
67          Security.addProvider(provider);
68      }
69  
70      /**
71       * Load permissions and certificates into the store based.
72       * @param permissions the PermissionSet from RepositorySettings.
73       * @param componentID the ID of the component using the PermissionStore. 
74       * @throws CertificateException in case a bad certificate data in PermissionSet.   
75       */
76      public void loadPermissions(PermissionSet permissions, String componentID) throws CertificateException {
77          if(permissions != null) {
78              Set<Operation> allowedOperations;
79              Set<String> allowedUsers;
80              for(Permission permission : permissions.getPermission()) {
81                  if(permission.getCertificate().getAllowedCertificateUsers() != null) {
82                      allowedUsers = new HashSet<String>();
83                      allowedUsers.addAll(permission.getCertificate().getAllowedCertificateUsers().getIDs());
84                  } else {
85                      allowedUsers = null;
86                  }
87  
88                  allowedOperations = new HashSet<Operation>();
89                  X509Certificate certificate = null;
90                  if(permission.getOperationPermission() != null) {
91                      for(OperationPermission perm : permission.getOperationPermission()) {
92                          if(perm.getAllowedComponents() == null ||
93                                  perm.getAllowedComponents().getIDs().contains(componentID)) {
94                              allowedOperations.add(perm.getOperation());
95                          }
96                      }
97                      if(!allowedOperations.isEmpty()) {
98                          certificate = makeCertificate(permission.getCertificate().getCertificateData());
99                      }
100                 }
101                 if(permission.getInfrastructurePermission().contains(InfrastructurePermission.MESSAGE_SIGNER)) {
102                     if(certificate == null) {
103                         certificate = makeCertificate(permission.getCertificate().getCertificateData());
104                     }
105                 }
106 
107                 if(certificate != null) {
108                     CertificateID certID = new CertificateID(certificate.getIssuerX500Principal(),
109                             certificate.getSerialNumber());
110                     CertificatePermission certificatePermission = new CertificatePermission(certificate, allowedOperations,
111                             allowedUsers);
112                     permissionMap.put(certID, certificatePermission);
113                 }
114             }
115         } else {
116             log.info("The provided PermissionSet was null");
117         }
118     }
119 
120     /**
121      * Retrieve the certificate based on the signerId.
122      * @param signer the identification data of the certificate to retrieve
123      * @return X509Certificate the certificate represented by the SignerId
124      * @throws PermissionStoreException if no certificate can be found based on the SignerId 
125      */
126     public X509Certificate getCertificate(SignerId signer) throws PermissionStoreException {
127         CertificateID certificateID = new CertificateID(signer.getIssuer(), signer.getSerialNumber());
128         CertificatePermission permission = permissionMap.get(certificateID);
129         if(permission != null) {
130             return permission.getCertificate();
131         } else {
132             throw new PermissionStoreException("Failed to find certificate for the requested signer:" + certificateID.toString());
133         }
134     }
135 
136     /**
137      * @param signer the signerId of the certificate used to sign the message.
138      * @param certificateUser the user that claims to have used the certificate.
139      * @return true, if the certificateUser has been registered for use of the certificate indicated by signer,
140      *         false otherwise.
141      * @throws PermissionStoreException in case no certificate has been registered for the given signerId
142      */
143     public boolean checkCertificateUser(SignerId signer, String certificateUser) throws PermissionStoreException {
144         CertificateID certificateID = new CertificateID(signer.getIssuer(), signer.getSerialNumber());
145         CertificatePermission certificatePermission = permissionMap.get(certificateID);
146         if(certificatePermission == null) {
147             throw new PermissionStoreException("Failed to find certificate and permissions for the requested signer: " +
148                     certificateID.toString());
149         } else {
150             return certificatePermission.isUserAllowed(certificateUser);
151         }
152     }
153 
154     /**
155      * Check to see if a certificate has the specified permission. The certificate is identified based 
156      * on the SignerId of the signature. 
157      * @return true if the requested permission is present for the certificate belonging to the signer, otherwise false.
158      * @throws PermissionStoreException in case no certificate and permission set can be found for the provided signer.
159      */
160     public boolean checkPermission(SignerId signer, Operation permission) throws PermissionStoreException {
161         CertificateID certificateID = new CertificateID(signer.getIssuer(), signer.getSerialNumber());
162         CertificatePermission certificatePermission = permissionMap.get(certificateID);
163         if(certificatePermission == null) {
164             throw new PermissionStoreException("Failed to find certificate and permissions for the requested signer: " +
165                     certificateID.toString());
166         } else {
167             return certificatePermission.hasPermission(permission);
168         }
169     }
170 
171     private X509Certificate makeCertificate(byte[] certificateData) throws CertificateException {
172         ByteArrayInputStream bs = new ByteArrayInputStream(certificateData);
173         X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance(
174                 SecurityModuleConstants.CertificateType).generateCertificate(bs);
175         try {
176             bs.close();
177         } catch (IOException e) {
178             log.debug("Failed to close ByteArrayInputStream", e);
179         }
180         return certificate;
181     }
182 
183     /**
184      * Class to contain a X509Certificate and the permissions associated with it.    
185      */
186     private final class CertificatePermission {
187         private final Set<Operation> permissions;
188         private final Set<String> allowedUsers;
189         private final X509Certificate certificate;
190 
191         /**
192          * Constructor
193          * @param certificate the certificate which permissions is to be represented.
194          * @param allowedOperations the allowed operations related to the certificate.
195          * @param allowedUsers the allowed users of this certificate, if users are not restricted provide null
196          */
197         public CertificatePermission(X509Certificate certificate, Collection<Operation> allowedOperations,
198                                      Collection<String> allowedUsers) {
199             if(allowedUsers == null) {
200                 this.allowedUsers = null;
201             } else {
202                 this.allowedUsers = new HashSet<String>();
203                 this.allowedUsers.addAll(allowedUsers);
204             }
205             this.permissions = new HashSet<Operation>();
206             this.certificate = certificate;
207             this.permissions.addAll(allowedOperations);
208         }
209 
210         /**
211          *  Test if a certain permission has been registered for this object. 
212          *  @param permission the permission to test for
213          *  @return true if the permission is registered, false otherwise.
214          */
215         public boolean hasPermission(Operation permission) {
216             return permissions.contains(permission);
217         }
218 
219         /**
220          *  Test if a certain certificate user has been registered as one allowed for use of this certificate.
221          *  @param certificateUser the user to test for
222          *  @return true if the user has been registered, false otherwise. 
223          */
224         public boolean isUserAllowed(String certificateUser) {
225             if(allowedUsers == null) {
226                 return true;
227             } else {
228                 return allowedUsers.contains(certificateUser);
229             }
230         }
231 
232         /**
233          * Retrieve the certificate from the object. 
234          * @return the X509Certificate from the object.
235          */
236         public X509Certificate getCertificate() {
237             return certificate;
238         }
239     }
240 
241 }