Merge "Add INFO.yaml file"
[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.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;
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 }