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";
55 protected static Symm symm;
57 protected final Path tokenPath;
58 protected final String tokenDir;
59 private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
61 public PersistFile(Access access, String sub_dir) throws CadiException, APIException {
63 tokenPath = Paths.get(access.getProperty(Config.CADI_TOKEN_DIR,"tokens"), sub_dir);
65 if(!Files.exists(tokenPath)) {
67 // Sorry Windows users, you need to secure your own paths
68 Files.createDirectories(tokenPath);
70 Set<PosixFilePermission> spfp = PosixFilePermissions.fromString("rwxr-x---");
71 Files.createDirectories(tokenPath,PosixFilePermissions.asFileAttribute(spfp));
74 tokenDir=tokenPath.toRealPath().toString();
75 } catch (IOException e) {
76 throw new CadiException(e);
78 synchronized(HASH_NO_MATCH) {
80 symm = Symm.obtain(access);
85 public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final String filename, final long expires) throws CadiException {
86 return writeDisk(df,t,cred,Paths.get(tokenDir,filename),expires);
89 public<T> Path writeDisk(final RosettaDF<T> df, final T t, final byte[] cred, final Path target, final long expires) throws CadiException {
90 // Make sure File is completely written before making accessible on disk... avoid corruption.
92 Path tpath = Files.createTempFile(tokenPath,target.getFileName().toString(), ".tmp");
93 final OutputStream dos = Files.newOutputStream(tpath, StandardOpenOption.CREATE,StandardOpenOption.WRITE);
95 // Write Expires so that we can read unencrypted.
96 for(int i=0;i<Long.SIZE;i+=8) {
97 dos.write((byte)((expires>>i)&0xFF));
100 symm.exec(new Symm.SyncExec<Void>() {
102 public Void exec(Encryption enc) throws Exception {
103 CipherOutputStream os = enc.outputStream(dos, true);
105 int size = cred==null?0:cred.length;
106 for(int i=0;i<Integer.SIZE;i+=8) {
107 os.write((byte)((size>>i)&0xFF));
112 df.newData().load(t).to(os);
114 // Note: Someone on the Web noticed that using a DataOutputStream would not full close out without a flush first,
115 // leaving files open.
118 } catch (IOException e) {
119 access.log(Level.INFO, "Note: Caught Exeption while flushing CipherStream. Handled.");
123 } catch (IOException e) {
124 access.log(Level.INFO, "Note: Caught Exeption while closing CipherStream. Handled.");
130 } catch(Exception e) {
131 throw new CadiException(e);
135 return Files.move(tpath, target, StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);
136 } catch (IOException e) {
137 throw new CadiException(e);
142 public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final String filename,final Holder<Path> hp, final Holder<Long> hl) throws CadiException {
144 hp.set(Paths.get(tokenDir,filename));
146 return readDisk(df,cred,hp.get(),hl);
149 public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final Path target, final Holder<Long> hexpired) throws CadiException {
152 if(Files.exists(target)) {
154 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
156 // Read Expired unencrypted
158 for(int i=0;i<Long.SIZE;i+=8) {
159 exp |= ((long)is.read()<<i);
163 t = symm.exec(new Symm.SyncExec<T>() {
165 public T exec(Encryption enc) throws Exception {
166 CipherInputStream dis = enc.inputStream(is,false);
169 for(int i=0;i<Integer.SIZE;i+=8) {
170 size |= ((int)dis.read()<<i);
173 throw new CadiException("Invalid size in Token Persistence");
174 } else if(cred!=null && size!=cred.length) {
175 throw new CadiException(HASH_NO_MATCH);
177 byte[] array = new byte[size];
179 for(int i=0;i<size;++i) {
180 if(cred[i]!=array[i]) {
181 throw new CadiException(HASH_NO_MATCH);
185 return df.newData().load(dis).asObject();
194 } catch (NoSuchFileException e) {
196 } catch (Exception e) {
197 throw new CadiException(e);
203 public long readExpiration(final Path target) throws CadiException {
205 if(Files.exists(target)) {
207 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
209 for(int i=0;i<Long.SIZE;i+=8) {
210 exp |= ((long)is.read()<<i);
216 } catch (Exception e) {
217 throw new CadiException(e);
223 public void deleteFromDisk(Path path) {
225 Files.deleteIfExists(path);
226 } catch (IOException e) {
227 access.log(Level.ERROR, e);
231 public void deleteFromDisk(String token) {
232 Path tpath = Paths.get(tokenDir,token);
234 Files.deleteIfExists(tpath);
235 } catch (IOException e) {
236 access.log(Level.ERROR, e);
240 public Path getPath(String filename) {
241 return Paths.get(tokenDir,filename);
244 public FileTime getFileTime(String filename, Holder<Path> hp) throws IOException {
247 hp.set(p=Paths.get(tokenDir,filename));
249 return Files.getLastModifiedTime(p);