Add a MassMail Batch Program
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / persist / PersistFile.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
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
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
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====================================================
19  *
20  */
21
22 package org.onap.aaf.cadi.persist;
23
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;
36 import java.util.Set;
37
38 import javax.crypto.CipherInputStream;
39 import javax.crypto.CipherOutputStream;
40
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.util.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;
50
51 public class PersistFile {
52
53     private static final String HASH_NO_MATCH = "Hash does not match in Persistence";
54     private static final Object LOCK = new Object();
55
56     protected static Symm symm;
57     public Access access;
58     protected final Path tokenPath;
59     protected final String tokenDir;
60     private static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
61
62     public PersistFile(Access access, String sub_dir) throws CadiException, APIException {
63         this.access = access;
64         tokenPath = Paths.get(access.getProperty(Config.CADI_TOKEN_DIR,"tokens"), sub_dir);
65         try {
66             if (!Files.exists(tokenPath)) {
67                 if (isWindows) {
68                     // Sorry Windows users, you need to secure your own paths
69                     Files.createDirectories(tokenPath);
70                 } else {
71                     Set<PosixFilePermission> spfp = PosixFilePermissions.fromString("rwxr-x---");
72                     Files.createDirectories(tokenPath,PosixFilePermissions.asFileAttribute(spfp));
73                 }
74             }
75             tokenDir=tokenPath.toRealPath().toString();
76         } catch (IOException e) {
77             throw new CadiException(e);
78         }
79         synchronized(LOCK) {
80             if (symm==null) {
81                 symm = Symm.obtain(access);
82             }
83         }
84     }
85
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);
88     }
89
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.
92         try {
93             Path tpath = Files.createTempFile(tokenPath,target.getFileName().toString(), ".tmp");
94             final OutputStream dos = Files.newOutputStream(tpath, StandardOpenOption.CREATE,StandardOpenOption.WRITE);
95                 try {
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));
99                 }
100
101                 symm.exec(new Symm.SyncExec<Void>() {
102                     @Override
103                     public Void exec(Encryption enc) throws Exception {
104                         CipherOutputStream os = enc.outputStream(dos, true);
105                         try {
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));
109                             }
110                             if (cred!=null) {
111                                 os.write(cred);
112                             }
113                             df.newData().load(t).to(os);
114                         } finally {
115                             // Note: Someone on the Web noticed that using a DataOutputStream would not full close out without a flush first,
116                             // leaving files open.
117                             try {
118                                 os.flush();
119                             } catch (IOException e) {
120                                 access.log(Level.INFO, "Note: Caught Exeption while flushing CipherStream.  Handled.");
121                             }
122                             try {
123                                 os.close();
124                             } catch (IOException e) {
125                                 access.log(Level.INFO, "Note: Caught Exeption while closing CipherStream.  Handled.");
126                             }
127                         }
128                         return null;
129                     }
130                 });
131             } catch (Exception e) {
132                 throw new CadiException(e);
133             } finally {
134                 dos.close();
135             }
136             return Files.move(tpath, target, StandardCopyOption.ATOMIC_MOVE,StandardCopyOption.REPLACE_EXISTING);
137         } catch (IOException e) {
138             throw new CadiException(e);
139         }
140
141     }
142
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 {
144         if (hp.get()==null) {
145             hp.set(Paths.get(tokenDir,filename));
146         }
147         return readDisk(df,cred,hp.get(),hl);
148     }
149
150     public <T> T readDisk(final RosettaDF<T> df, final byte[] cred, final Path target, final Holder<Long> hexpired) throws CadiException {
151         // Try from Disk
152         T t = null;
153         if (Files.exists(target)) {
154             try {
155                 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
156                 try {
157                     // Read Expired unencrypted
158                     long exp=0;
159                     for (int i=0;i<Long.SIZE;i+=8) {
160                         exp |= ((long)is.read()<<i);
161                     }
162                     hexpired.set(exp);
163
164                     t = symm.exec(new Symm.SyncExec<T>() {
165                         @Override
166                         public T exec(Encryption enc) throws Exception {
167                             CipherInputStream dis = enc.inputStream(is,false);
168                             try {
169                                 int size=0;
170                                 for (int i=0;i<Integer.SIZE;i+=8) {
171                                     size |= ((int)dis.read()<<i);
172                                 }
173                                 if (size>256) {
174                                     throw new CadiException("Invalid size in Token Persistence");
175                                 } else if (cred!=null && size!=cred.length) {
176                                     throw new CadiException(HASH_NO_MATCH);
177                                 }
178                                 if (cred!=null) {
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);
184                                             }
185                                         }
186                                     }
187                                 }
188                                 return df.newData().load(dis).asObject();
189                             } finally {
190                                 dis.close();
191                             }
192                         }
193                     });
194                 } finally {
195                     is.close();
196                 }
197             } catch (NoSuchFileException e) {
198                 return t;
199             } catch (Exception e) {
200                 throw new CadiException(e);
201             }
202         }
203         return t;
204     }
205
206     public long readExpiration(final Path target) throws CadiException {
207         long exp=0L;
208         if (Files.exists(target)) {
209             try {
210                 final InputStream is = Files.newInputStream(target,StandardOpenOption.READ);
211                 try {
212                     for (int i=0;i<Long.SIZE;i+=8) {
213                         exp |= ((long)is.read()<<i);
214                     }
215                 } finally {
216                     is.close();
217                 }
218                 return exp;
219             } catch (Exception e) {
220                 throw new CadiException(e);
221             }
222         }
223         return exp;
224     }
225
226     public void deleteFromDisk(Path path) {
227         try {
228             Files.deleteIfExists(path);
229         } catch (IOException e) {
230             access.log(Level.ERROR, e);
231         }
232     }
233
234     public void deleteFromDisk(String token) {
235         Path tpath = Paths.get(tokenDir,token);
236         try {
237             Files.deleteIfExists(tpath);
238         } catch (IOException e) {
239             access.log(Level.ERROR, e);
240         }
241     }
242
243     public Path getPath(String filename) {
244         return Paths.get(tokenDir,filename);
245     }
246
247     public FileTime getFileTime(String filename, Holder<Path> hp) throws IOException {
248         Path p = hp.get();
249         if (p==null) {
250             hp.set(p=Paths.get(tokenDir,filename));
251         }
252         return Files.getLastModifiedTime(p);
253     }
254
255 }