Refactor JCR exceptions.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / security / JcrKeyring.java
index 04e3eb9db76a99ff9d90113477de5cbf8f60fea5..c23db194dbca0e13e275cbaf7c060c1fec37ac85 100644 (file)
@@ -1,18 +1,3 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *         http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
 package org.argeo.cms.security;
 
 import java.io.ByteArrayInputStream;
@@ -22,11 +7,13 @@ import java.io.InputStream;
 import java.io.Reader;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.NoSuchAlgorithmException;
 import java.security.Provider;
 import java.security.SecureRandom;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
+import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.jcr.Binary;
@@ -41,13 +28,13 @@ import javax.jcr.query.Query;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.argeo.api.NodeConstants;
+import org.argeo.api.NodeUtils;
+import org.argeo.api.security.PBEKeySpecCallback;
 import org.argeo.cms.ArgeoNames;
 import org.argeo.cms.ArgeoTypes;
-import org.argeo.cms.CmsException;
-import org.argeo.jcr.ArgeoJcrException;
+import org.argeo.jcr.JcrException;
 import org.argeo.jcr.JcrUtils;
-import org.argeo.node.NodeUtils;
-import org.argeo.node.security.PBEKeySpecCallback;
 
 /** JCR based implementation of a keyring */
 public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
@@ -67,6 +54,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
        private String cipherName = DEFAULT_CIPHER_NAME;
 
        private final Repository repository;
+       // TODO remove thread local session ; open a session each time
        private ThreadLocal<Session> sessionThreadLocal = new ThreadLocal<Session>() {
 
                @Override
@@ -84,13 +72,13 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
         * handling the PBE callback. We keep one per thread in case multiple users are
         * accessing the same instance of a keyring.
         */
-//     private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
-//
-//             @Override
-//             protected Node initialValue() {
-//                     return null;
-//             }
-//     };
+       // private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
+       //
+       // @Override
+       // protected Node initialValue() {
+       // return null;
+       // }
+       // };
 
        public JcrKeyring(Repository repository) {
                this.repository = repository;
@@ -107,23 +95,26 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
 
        private Session login() {
                try {
-                       return repository.login();
+                       return repository.login(NodeConstants.HOME_WORKSPACE);
                } catch (RepositoryException e) {
-                       throw new CmsException("Cannot login key ring session", e);
+                       throw new JcrException("Cannot login key ring session", e);
                }
        }
 
        @Override
        protected synchronized Boolean isSetup() {
+               Session session = null;
                try {
-//                     if (notYetSavedKeyring.get() != null)
-//                             return true;
-
-                       session().refresh(true);
-                       Node userHome = NodeUtils.getUserHome(session());
+                       // if (notYetSavedKeyring.get() != null)
+                       // return true;
+                       session = session();
+                       session.refresh(true);
+                       Node userHome = NodeUtils.getUserHome(session);
                        return userHome.hasNode(ARGEO_KEYRING);
                } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot check whether keyring is setup", e);
+                       throw new JcrException("Cannot check whether keyring is setup", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
                }
        }
 
@@ -136,7 +127,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        Node userHome = NodeUtils.getUserHome(session());
                        Node keyring;
                        if (userHome.hasNode(ARGEO_KEYRING)) {
-                               throw new CmsException("Keyring already set up");
+                               throw new IllegalArgumentException("Keyring already set up");
                        } else {
                                keyring = userHome.addNode(ARGEO_KEYRING);
                        }
@@ -154,8 +145,10 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        }
                        try (InputStream in = new ByteArrayInputStream(salt);) {
                                binary = session().getValueFactory().createBinary(in);
+                               keyring.setProperty(ARGEO_SALT, binary);
+                       } catch (IOException e) {
+                               throw new RuntimeException("Cannot set keyring salt", e);
                        }
-                       keyring.setProperty(ARGEO_SALT, binary);
 
                        Integer iterationCount = username.length() * iterationCountFactor;
                        keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
@@ -177,9 +170,9 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        // binary = session().getValueFactory().createBinary(in);
                        // keyring.setProperty(ARGEO_PASSWORD, binary);
 
-//                     notYetSavedKeyring.set(keyring);
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot setup keyring", e);
+                       // notYetSavedKeyring.set(keyring);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot setup keyring", e);
                } finally {
                        JcrUtils.closeQuietly(binary);
                        // IOUtils.closeQuietly(in);
@@ -189,16 +182,18 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
 
        @Override
        protected synchronized void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
+               Session session = null;
                try {
-                       session().refresh(true);
-                       Node userHome = NodeUtils.getUserHome(session());
+                       session = session();
+                       session.refresh(true);
+                       Node userHome = NodeUtils.getUserHome(session);
                        Node keyring;
                        if (userHome.hasNode(ARGEO_KEYRING))
                                keyring = userHome.getNode(ARGEO_KEYRING);
-//                     else if (notYetSavedKeyring.get() != null)
-//                             keyring = notYetSavedKeyring.get();
+                       // else if (notYetSavedKeyring.get() != null)
+                       // keyring = notYetSavedKeyring.get();
                        else
-                               throw new ArgeoJcrException("Keyring not setup");
+                               throw new IllegalStateException("Keyring not setup");
 
                        pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY).getString(),
                                        JcrUtils.getBinaryAsBytes(keyring.getProperty(ARGEO_SALT)),
@@ -206,10 +201,12 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                                        (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
                                        keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION).getString());
 
-//                     if (notYetSavedKeyring.get() != null)
-//                             notYetSavedKeyring.remove();
+                       // if (notYetSavedKeyring.get() != null)
+                       // notYetSavedKeyring.remove();
                } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot handle key spec callback", e);
+                       throw new JcrException("Cannot handle key spec callback", e);
+               } finally {
+                       JcrUtils.logoutQuietly(session);
                }
        }
 
@@ -228,7 +225,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        if (!session().nodeExists(path)) {
                                String parentPath = JcrUtils.parentPath(path);
                                if (!session().nodeExists(parentPath))
-                                       throw new ArgeoJcrException("No parent node of " + path);
+                                       throw new IllegalStateException("No parent node of " + path);
                                Node parentNode = session().getNode(parentPath);
                                node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
                        } else {
@@ -248,7 +245,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        // session().save();
                        // }
                } catch (RepositoryException e) {
-                       throw new ArgeoJcrException("Cannot encrypt", e);
+                       throw new JcrException("Cannot encrypt", e);
                } finally {
                        try {
                                unencrypted.close();
@@ -279,25 +276,16 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        } finally {
                                JcrUtils.closeQuietly(binary);
                        }
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot encrypt", e);
-               } finally {
-                       try {
-                               unencrypted.close();
-                       } catch (IOException e) {
-                               // silent
-                       }
-                       // IOUtils.closeQuietly(unencrypted);
-                       // IOUtils.closeQuietly(in);
-                       // JcrUtils.closeQuietly(binary);
-                       // JcrUtils.logoutQuietly(session());
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot encrypt", e);
+               } catch (GeneralSecurityException | IOException e) {
+                       throw new RuntimeException("Cannot encrypt", e);
                }
        }
 
        @Override
        protected synchronized InputStream decrypt(String path) {
                Binary binary = null;
-               // InputStream encrypted = null;
                try {
                        session().refresh(true);
                        if (!session().nodeExists(path)) {
@@ -311,11 +299,11 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                                Node node = session().getNode(path);
                                return decrypt(secretKey, cipher, node);
                        }
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot decrypt", e);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot decrypt", e);
+               } catch (GeneralSecurityException | IOException e) {
+                       throw new RuntimeException("Cannot decrypt", e);
                } finally {
-                       // IOUtils.closeQuietly(encrypted);
-                       // IOUtils.closeQuietly(reader);
                        JcrUtils.closeQuietly(binary);
                        JcrUtils.logoutQuietly(session());
                }
@@ -339,7 +327,7 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                try {
                        Node userHome = NodeUtils.getUserHome(session());
                        if (!userHome.hasNode(ARGEO_KEYRING))
-                               throw new ArgeoJcrException("Keyring not setup");
+                               throw new IllegalArgumentException("Keyring not setup");
                        Node keyring = userHome.getNode(ARGEO_KEYRING);
                        String cipherName = keyring.getProperty(ARGEO_CIPHER).getString();
                        Provider securityProvider = getSecurityProvider();
@@ -349,8 +337,12 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                        else
                                cipher = Cipher.getInstance(cipherName, securityProvider);
                        return cipher;
-               } catch (Exception e) {
-                       throw new ArgeoJcrException("Cannot get cipher", e);
+               } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+                       throw new IllegalArgumentException("Cannot get cipher", e);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot get cipher", e);
+               } finally {
+
                }
        }
 
@@ -369,8 +361,10 @@ public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
                                if (log.isDebugEnabled())
                                        log.debug("Converted keyring encrypted value of " + node.getPath());
                        }
-               } catch (RepositoryException | GeneralSecurityException e) {
-                       throw new CmsException("Cannot change JCR keyring password", e);
+               } catch (GeneralSecurityException e) {
+                       throw new RuntimeException("Cannot change JCR keyring password", e);
+               } catch (RepositoryException e) {
+                       throw new JcrException("Cannot change JCR keyring password", e);
                } finally {
                        JcrUtils.logoutQuietly(session);
                }