2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6 * ===========================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END====================================================
22 package org.onap.aaf.cadi.persist;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.nio.file.Files;
28 import java.nio.file.NoSuchFileException;
29 import java.nio.file.Path;
30 import java.nio.file.Paths;
31 import java.nio.file.StandardCopyOption;
32 import java.nio.file.StandardOpenOption;
33 import java.nio.file.attribute.FileTime;
34 import java.nio.file.attribute.PosixFilePermission;
35 import java.nio.file.attribute.PosixFilePermissions;
38 import javax.crypto.CipherInputStream;
39 import javax.crypto.CipherOutputStream;
41 import org.onap.aaf.cadi.Access;
42 import org.onap.aaf.cadi.CadiException;
43 import org.onap.aaf.cadi.Symm;
44 import org.onap.aaf.cadi.Access.Level;
45 import org.onap.aaf.cadi.Symm.Encryption;
46 import org.onap.aaf.cadi.client.Holder;
47 import org.onap.aaf.cadi.config.Config;
48 import org.onap.aaf.misc.env.APIException;
49 import org.onap.aaf.misc.rosetta.env.RosettaDF;
51 public class PersistFile {
53 private static final String HASH_NO_MATCH = "Hash does not match in Persistence";
54 private static final Object LOCK = new Object();
56 protected static Symm symm;
58 protected final Path tokenPath;
59 protected final String tokenDir;
60 private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
62 public PersistFile(Access access, String sub_dir) throws CadiException, APIException {
64 tokenPath = Paths.get(access.getProperty(Config.CADI_TOKEN_DIR,"tokens"), sub_dir);
66 if(!Files.exists(tokenPath)) {
68 // Sorry Windows users, you need to secure your own paths
69 Files.createDirectories(tokenPath);
71 Set<PosixFilePermission> spfp = PosixFilePermissions.fromString("rwxr-x---");
72 Files.createDirectories(tokenPath,PosixFilePermissions.asFileAttribute(spfp));
75 tokenDir=tokenPath.toRealPath().toString();
76 } catch (IOException e) {
77 throw new CadiException(e);
81 symm = Symm.obtain(access);
86 public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final String filename, final long expires) throws CadiException {
87 return writeDisk(df,t,cred,Paths.get(tokenDir,filename),expires);
90 public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final Path target, final long expires) throws CadiException {
91 // Make sure File is completely written before making accessible on disk... avoid corruption.
93 Path tpath = Files.createTempFile(tokenPath,target.getFileName().toString(), ".tmp");
94 final OutputStream dos = Files.newOutputStream(tpath, StandardOpenOption.CREATE,StandardOpenOption.WRITE);
96 // Write Expires so that we can read unencrypted.
97 for(int i=0;i<Long.SIZE;i+=8) {
98 dos.write((byte)((expires>>i)&0xFF));
101 symm.exec(new Symm.SyncExec<Void>() {
103 public Void exec(Encryption enc) throws Exception {
104 CipherOutputStream os = enc.outputStream(dos, true);
106 int size = cred==null?0:cred.length;
107 for(int i=0;i<Integer.SIZE;i+=8) {
108 os.write((byte)((size>>i)&0xFF));
113 df.newData().load(t).to(os);
115 // Note: Someone on the Web noticed that using a DataOutputStream would not full close out without a flush first,
116 // leaving files open.
119 } catch (IOException e) {
120 access.log(Level.INFO, "Note: Caught Exeption while flushing CipherStream. Handled.");
124 } catch (IOException e) {
125 access.log(Level.INFO, "Note: Caught Exeption while closing CipherStream. Handled.");
131 } catch(Exception e) {
132 throw new CadiException(e);
136 return Files.move(tpath, target, StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);
137 } catch (IOException e) {
138 throw new CadiException(e);
143 public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final String filename,final Holder<Path> hp, final Holder<Long> hl) throws CadiException {
145 hp.set(Paths.get(tokenDir,filename));
147 return readDisk(df,cred,hp.get(),hl);
150 public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final Path target, final Holder<Long> hexpired) throws CadiException {
153 if(Files.exists(target)) {
155 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
157 // Read Expired unencrypted
159 for(int i=0;i<Long.SIZE;i+=8) {
160 exp |= ((long)is.read()<<i);
164 t = symm.exec(new Symm.SyncExec<T>() {
166 public T exec(Encryption enc) throws Exception {
167 CipherInputStream dis = enc.inputStream(is,false);
170 for(int i=0;i<Integer.SIZE;i+=8) {
171 size |= ((int)dis.read()<<i);
174 throw new CadiException("Invalid size in Token Persistence");
175 } else if(cred!=null && size!=cred.length) {
176 throw new CadiException(HASH_NO_MATCH);
179 byte[] array = new byte[size];
180 if(dis.read(array)>0) {
181 for(int i=0;i<size;++i) {
182 if(cred[i]!=array[i]) {
183 throw new CadiException(HASH_NO_MATCH);
188 return df.newData().load(dis).asObject();
197 } catch (NoSuchFileException e) {
199 } catch (Exception e) {
200 throw new CadiException(e);
206 public long readExpiration(final Path target) throws CadiException {
208 if(Files.exists(target)) {
210 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
212 for(int i=0;i<Long.SIZE;i+=8) {
213 exp |= ((long)is.read()<<i);
219 } catch (Exception e) {
220 throw new CadiException(e);
226 public void deleteFromDisk(Path path) {
228 Files.deleteIfExists(path);
229 } catch (IOException e) {
230 access.log(Level.ERROR, e);
234 public void deleteFromDisk(String token) {
235 Path tpath = Paths.get(tokenDir,token);
237 Files.deleteIfExists(tpath);
238 } catch (IOException e) {
239 access.log(Level.ERROR, e);
243 public Path getPath(String filename) {
244 return Paths.get(tokenDir,filename);
247 public FileTime getFileTime(String filename, Holder<Path> hp) throws IOException {
250 hp.set(p=Paths.get(tokenDir,filename));
252 return Files.getLastModifiedTime(p);