001/* 002 * (C) Copyright 2011 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 * Wojciech Sulejman 018 */ 019package org.nuxeo.ecm.platform.signature.core.user; 020 021import java.io.ByteArrayInputStream; 022import java.io.ByteArrayOutputStream; 023import java.security.KeyStore; 024import java.security.cert.X509Certificate; 025import java.util.HashMap; 026import java.util.Map; 027 028import javax.security.auth.login.LoginContext; 029import javax.security.auth.login.LoginException; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.common.utils.Base64; 034import org.nuxeo.ecm.core.api.DocumentModel; 035import org.nuxeo.ecm.core.api.NuxeoException; 036import org.nuxeo.ecm.directory.DirectoryException; 037import org.nuxeo.ecm.directory.Session; 038import org.nuxeo.ecm.directory.api.DirectoryService; 039import org.nuxeo.ecm.platform.signature.api.exception.CertException; 040import org.nuxeo.ecm.platform.signature.api.pki.CertService; 041import org.nuxeo.ecm.platform.signature.api.pki.RootService; 042import org.nuxeo.ecm.platform.signature.api.user.AliasType; 043import org.nuxeo.ecm.platform.signature.api.user.AliasWrapper; 044import org.nuxeo.ecm.platform.signature.api.user.CNField; 045import org.nuxeo.ecm.platform.signature.api.user.CUserService; 046import org.nuxeo.ecm.platform.signature.api.user.UserInfo; 047import org.nuxeo.runtime.api.Framework; 048import org.nuxeo.runtime.model.ComponentInstance; 049import org.nuxeo.runtime.model.DefaultComponent; 050 051/** 052 * Base implementation of the user certificate service. 053 * 054 * @author <a href="mailto:ws@nuxeo.com">Wojciech Sulejman</a> 055 */ 056public class CUserServiceImpl extends DefaultComponent implements CUserService { 057 058 private static final Log LOG = LogFactory.getLog(CUserServiceImpl.class); 059 060 private static final String CERTIFICATE_DIRECTORY_NAME = "certificate"; 061 062 protected RootService rootService; 063 064 protected CertService certService; 065 066 /** 067 * Configurable country code 068 */ 069 protected String countryCode; 070 071 /** 072 * Configurable organization name 073 */ 074 protected String organization; 075 076 /** 077 * Configurable organizational unit name 078 */ 079 protected String organizationalUnit; 080 081 @Override 082 public UserInfo getUserInfo(DocumentModel userModel) throws CertException { 083 UserInfo userInfo = null; 084 String userID = (String) userModel.getPropertyValue("user:username"); 085 String firstName = (String) userModel.getPropertyValue("user:firstName"); 086 String lastName = (String) userModel.getPropertyValue("user:lastName"); 087 String email = (String) userModel.getPropertyValue("user:email"); 088 089 Map<CNField, String> userFields = new HashMap<CNField, String>(); 090 091 userFields.put(CNField.C, countryCode); 092 userFields.put(CNField.O, organization); 093 userFields.put(CNField.OU, organizationalUnit); 094 095 userFields.put(CNField.CN, firstName + " " + lastName); 096 userFields.put(CNField.Email, email); 097 userFields.put(CNField.UserID, userID); 098 userInfo = new UserInfo(userFields); 099 return userInfo; 100 } 101 102 @Override 103 public KeyStore getUserKeystore(String userID, String userKeystorePassword) throws CertException { 104 // Log in as system user 105 LoginContext lc; 106 try { 107 lc = Framework.login(); 108 } catch (LoginException e) { 109 throw new NuxeoException("Cannot log in as system user", e); 110 } 111 try { 112 // Open directory session 113 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 114 KeyStore keystore = null; 115 DocumentModel entry = session.getEntry(userID); 116 if (entry != null) { 117 String keystore64Encoded = (String) entry.getPropertyValue("cert:keystore"); 118 byte[] keystoreBytes = Base64.decode(keystore64Encoded); 119 ByteArrayInputStream byteIS = new ByteArrayInputStream(keystoreBytes); 120 keystore = getCertService().getKeyStore(byteIS, userKeystorePassword); 121 } else { 122 throw new CertException("No directory entry for " + userID); 123 } 124 return keystore; 125 } 126 } finally { 127 try { 128 // Login context may be null in tests 129 if (lc != null) { 130 lc.logout(); 131 } 132 } catch (LoginException e) { 133 throw new NuxeoException("Cannot log out system user", e); 134 } 135 } 136 } 137 138 @Override 139 public DocumentModel createCertificate(DocumentModel user, String userKeyPassword) throws CertException { 140 // Log in as system user 141 LoginContext lc; 142 try { 143 lc = Framework.login(); 144 } catch (LoginException e) { 145 throw new NuxeoException("Cannot log in as system user", e); 146 } 147 try { 148 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 149 String userKeystorePassword = userKeyPassword; 150 DocumentModel certificate = null; 151 152 // create an entry in the directory 153 String userID = (String) user.getPropertyValue("user:username"); 154 155 // make sure that no certificates are associated with the 156 // current userid 157 boolean certificateExists = session.hasEntry(userID); 158 if (certificateExists) { 159 throw new CertException(userID + " already has a certificate"); 160 } 161 162 LOG.info("Starting certificate generation for: " + userID); 163 Map<String, Object> map = new HashMap<String, Object>(); 164 map.put("userid", userID); 165 166 // add a keystore to a directory entry 167 KeyStore keystore = getCertService().initializeUser(getUserInfo(user), userKeyPassword); 168 ByteArrayOutputStream byteOS = new ByteArrayOutputStream(); 169 getCertService().storeCertificate(keystore, byteOS, userKeystorePassword); 170 String keystore64Encoded = Base64.encodeBytes(byteOS.toByteArray()); 171 map.put("keystore", keystore64Encoded); 172 map.put("certificate", getUserCertInfo(keystore, user)); 173 map.put("keypassword", userKeyPassword); 174 certificate = session.createEntry(map); 175 return certificate; 176 } catch (DirectoryException e) { 177 LOG.error(e); 178 throw new CertException(e); 179 } 180 } finally { 181 try { 182 // Login context may be null in tests 183 if (lc != null) { 184 lc.logout(); 185 } 186 } catch (LoginException e) { 187 throw new NuxeoException("Cannot log out system user", e); 188 } 189 } 190 } 191 192 protected static DirectoryService getDirectoryService() { 193 return Framework.getService(DirectoryService.class); 194 } 195 196 @Override 197 public String getUserCertInfo(DocumentModel user, String userKeyPassword) throws CertException { 198 String userKeystorePassword = userKeyPassword; 199 String userID = (String) user.getPropertyValue("user:username"); 200 KeyStore keystore = getUserKeystore(userID, userKeystorePassword); 201 return getUserCertInfo(keystore, user); 202 } 203 204 private String getUserCertInfo(KeyStore keystore, DocumentModel user) throws CertException { 205 String userCertInfo = null; 206 if (null != keystore) { 207 String userID = (String) user.getPropertyValue("user:username"); 208 AliasWrapper alias = new AliasWrapper(userID); 209 X509Certificate certificate = getCertService().getCertificate(keystore, alias.getId(AliasType.CERT)); 210 userCertInfo = certificate.getSubjectDN() + " valid till: " + certificate.getNotAfter(); 211 } 212 return userCertInfo; 213 } 214 215 @Override 216 public DocumentModel getCertificate(String userID) { 217 // Log in as system user 218 LoginContext lc; 219 try { 220 lc = Framework.login(); 221 } catch (LoginException e) { 222 throw new NuxeoException("Cannot log in as system user", e); 223 } 224 try { 225 // Open directory session 226 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 227 DocumentModel certificate = session.getEntry(userID); 228 return certificate; 229 } 230 } finally { 231 try { 232 // Login context may be null in tests 233 if (lc != null) { 234 lc.logout(); 235 } 236 } catch (LoginException e) { 237 throw new NuxeoException("Cannot log out system user", e); 238 } 239 } 240 } 241 242 @Override 243 public byte[] getRootCertificateData() { 244 byte[] certificateData = getRootService().getRootPublicCertificate(); 245 return certificateData; 246 } 247 248 @Override 249 public boolean hasCertificate(String userID) throws CertException { 250 // Log in as system user 251 LoginContext lc; 252 try { 253 lc = Framework.login(); 254 } catch (LoginException e) { 255 throw new NuxeoException("Cannot log in as system user", e); 256 } 257 try { 258 // Open directory session 259 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 260 return session.getEntry(userID) != null; 261 } 262 } finally { 263 try { 264 // Login context may be null in tests 265 if (lc != null) { 266 lc.logout(); 267 } 268 } catch (LoginException e) { 269 throw new NuxeoException("Cannot log out system user", e); 270 } 271 } 272 } 273 274 @Override 275 public void deleteCertificate(String userID) throws CertException { 276 // Log in as system user 277 LoginContext lc; 278 try { 279 lc = Framework.login(); 280 } catch (LoginException e) { 281 throw new NuxeoException("Cannot log in as system user", e); 282 } 283 try { 284 // Open directory session 285 try (Session session = getDirectoryService().open(CERTIFICATE_DIRECTORY_NAME)) { 286 DocumentModel certEntry = session.getEntry(userID); 287 session.deleteEntry(certEntry); 288 assert (null == session.getEntry(userID)); 289 } 290 } finally { 291 try { 292 // Login context may be null in tests 293 if (lc != null) { 294 lc.logout(); 295 } 296 } catch (LoginException e) { 297 throw new NuxeoException("Cannot log out system user", e); 298 } 299 } 300 } 301 302 @Override 303 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 304 if (contribution instanceof CUserDescriptor) { 305 CUserDescriptor desc = (CUserDescriptor) contribution; 306 countryCode = desc.getCountryCode(); 307 organization = desc.getOrganization(); 308 organizationalUnit = desc.getOrganizationalUnit(); 309 } 310 } 311 312 protected CertService getCertService() { 313 if (certService == null) { 314 certService = Framework.getService(CertService.class); 315 } 316 return certService; 317 } 318 319 protected RootService getRootService() { 320 if (rootService == null) { 321 rootService = Framework.getService(RootService.class); 322 } 323 return rootService; 324 } 325}