5acc836e7d18210b1602242510c7e15d4584f93f
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / cass / CredDAO.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2019 IBM.
7  * ===========================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END====================================================
20  *
21  */
22
23 package org.onap.aaf.auth.dao.cass;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataInputStream;
27 import java.io.DataOutputStream;
28 import java.io.IOException;
29 import java.nio.ByteBuffer;
30 import java.security.SecureRandom;
31 import java.util.Date;
32 import java.util.List;
33
34 import org.onap.aaf.auth.dao.Bytification;
35 import org.onap.aaf.auth.dao.CIDAO;
36 import org.onap.aaf.auth.dao.Cached;
37 import org.onap.aaf.auth.dao.CassDAOImpl;
38 import org.onap.aaf.auth.dao.Loader;
39 import org.onap.aaf.auth.dao.Streamer;
40 import org.onap.aaf.auth.env.AuthzTrans;
41 import org.onap.aaf.auth.layer.Result;
42 import org.onap.aaf.misc.env.APIException;
43 import org.onap.aaf.misc.env.util.Chrono;
44
45 import com.datastax.driver.core.Cluster;
46 import com.datastax.driver.core.Row;
47
48 /**
49  * CredDAO manages credentials. 
50  * @author Jonathan
51  * Date: 7/19/13
52  */
53 public class CredDAO extends CassDAOImpl<AuthzTrans,CredDAO.Data> {
54     public static final String TABLE = "cred";
55     public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F
56     public static final int RAW = -1;
57     public static final int NONE = 0;
58     public static final int FQI = 10;
59     public static final int BASIC_AUTH = 1;
60     public static final int BASIC_AUTH_SHA256 = 2;
61     public static final int CERT_SHA256_RSA =200;
62     public static final SecureRandom srand = new SecureRandom();
63     
64     private HistoryDAO historyDAO;
65     private CIDAO<AuthzTrans> infoDAO;
66     private PSInfo psNS;
67     private PSInfo psID;
68     private PSInfo psIDBath;
69     
70     public CredDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
71         super(trans, CredDAO.class.getSimpleName(),cluster, keyspace, Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
72         init(trans);
73     }
74
75     public CredDAO(AuthzTrans trans, HistoryDAO hDao, CacheInfoDAO ciDao) throws APIException, IOException {
76         super(trans, CredDAO.class.getSimpleName(),hDao, Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
77         historyDAO = hDao;
78         infoDAO = ciDao;
79         init(trans);
80     }
81
82     public static final int KEYLIMIT = 3;
83     public static class Data extends CacheableData implements Bytification {
84         
85         public String                   id;
86         public Integer                  type;
87         public Date                     expires;
88         public Integer                  other;
89         public String                   ns;
90         public String                    tag;
91         public String                    notes;
92         public ByteBuffer               cred;  //   this is a blob in cassandra
93
94
95         @Override
96         public int[] invalidate(Cached<?,?> cache) {
97             return new int[] {
98                 seg(cache,id) // cache is for all entities
99             };
100         }
101         
102         @Override
103         public ByteBuffer bytify() throws IOException {
104             ByteArrayOutputStream baos = new ByteArrayOutputStream();
105             CredLoader.deflt.marshal(this,new DataOutputStream(baos));
106             return ByteBuffer.wrap(baos.toByteArray());
107         }
108         
109         @Override
110         public void reconstitute(ByteBuffer bb) throws IOException {
111             CredLoader.deflt.unmarshal(this, toDIS(bb));
112         }
113
114         public String toString() {
115             return id + ' ' + type + ' ' + Chrono.dateTime(expires);
116         }
117     }
118
119     public static class CredLoader extends Loader<Data> implements Streamer<Data>{
120         public static final int MAGIC=153323443;
121         public static final int VERSION=2;
122         public static final int BUFF_SIZE=48; // Note: 
123
124         public static final CredLoader deflt = new CredLoader(KEYLIMIT);
125         public CredLoader(int keylimit) {
126             super(keylimit);
127         }
128
129         @Override
130         public Data load(Data data, Row row) {
131             data.id = row.getString(0);
132             data.type = row.getInt(1);    // NOTE: in datastax driver,  If the int value is NULL, 0 is returned!
133             data.expires = row.getTimestamp(2);
134             data.other = row.getInt(3);
135             data.ns = row.getString(4);     
136             data.tag = row.getString(5);
137             data.notes = row.getString(6);
138             data.cred = row.getBytesUnsafe(7);            
139             return data;
140         }
141
142         @Override
143         protected void key(Data data, int _idx, Object[] obj) {
144             int idx = _idx;
145
146             obj[idx] = data.id;
147             obj[++idx] = data.type;
148             obj[++idx] = data.expires;
149         }
150
151         @Override
152         protected void body(Data data, int idx, Object[] obj) {
153             int i;
154             i=idx;
155             obj[i] = data.other;
156             obj[++i] = data.ns;
157             obj[++i] = data.tag;
158             obj[++i] = data.notes;
159             obj[++i] = data.cred;
160         }
161
162         @Override
163         public void marshal(Data data, DataOutputStream os) throws IOException {
164             writeHeader(os,MAGIC,VERSION);
165             writeString(os, data.id);
166             os.writeInt(data.type);    
167             os.writeLong(data.expires==null?-1:data.expires.getTime());
168             os.writeInt(data.other==null?0:data.other);
169             writeString(os, data.ns);
170             writeString(os, data.tag);
171             writeString(os, data.notes);
172             if (data.cred==null) {
173                 os.writeInt(-1);
174             } else {
175                 int l = data.cred.limit()-data.cred.position();
176                 os.writeInt(l);
177                 os.write(data.cred.array(),data.cred.position(),l);
178             }
179         }
180
181         @Override
182         public void unmarshal(Data data, DataInputStream is) throws IOException {
183             /*int version = */readHeader(is,MAGIC,VERSION);
184             // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
185             byte[] buff = new byte[BUFF_SIZE];
186             data.id = readString(is,buff);
187             data.type = is.readInt();
188             
189             long l = is.readLong();
190             data.expires = l<0?null:new Date(l);
191             data.other = is.readInt();
192             data.ns = readString(is,buff);
193             data.tag = readString(is,buff);
194             data.notes = readString(is,buff);
195             
196             int i = is.readInt();
197             data.cred=null;
198             if (i>=0) {
199                 byte[] bytes = new byte[i]; // a bit dangerous, but lessened because of all the previous sized data reads
200                 int read = is.read(bytes);
201                 if (read>0) {
202                     data.cred = ByteBuffer.wrap(bytes);
203                 }
204             }
205         }
206     }
207
208     private void init(AuthzTrans trans) throws APIException, IOException {
209         // Set up sub-DAOs
210         if (historyDAO==null) {
211             historyDAO = new HistoryDAO(trans,this);
212         }
213         if (infoDAO==null) {
214             infoDAO = new CacheInfoDAO(trans,this);
215         }
216         
217
218         String[] helpers = setCRUD(trans, TABLE, Data.class, CredLoader.deflt);
219         
220         psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
221                 " WHERE ns = ?", CredLoader.deflt,readConsistency);
222         
223         psID = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
224                 " WHERE id = ?", CredLoader.deflt,readConsistency);
225         
226         // NOTE: (type) in ((1),(2)) is valid for Cass 2.1.14.  After 2.1.14, more obvious
227         // syntax of type in (1,2) is available
228         // ALSO, 1 & 2 STAND FOR BASIC_AUTH (MD5) AND BASIC_AUTH_SHA256(with salt).
229         // If more Basic Auth Protocols become available, add here but do NOT
230         // add X509, and there can be man Certs, and we don't need to read them every time, or
231         // as discovered, or provide CASS Outage due to too many Certs to read.
232         psIDBath = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
233                 " WHERE id = ? and (type) in ((1),(2))", CredLoader.deflt,readConsistency);
234     }
235     
236     /* (non-Javadoc)
237      * @see org.onap.aaf.auth.dao.CassDAOImpl#create(org.onap.aaf.misc.env.TransStore, java.lang.Object)
238      */
239     @Override
240     public Result<Data> create(AuthzTrans trans, Data data) {
241         if(data.tag == null) {
242             if(data.type==0) {
243                 data.tag="PlaceHolder";
244             } else {
245                 long l = srand.nextLong();
246                 data.tag = Long.toHexString(l);
247             }
248         }
249         return super.create(trans, data);
250     }
251
252     public Result<List<Data>> readNS(AuthzTrans trans, String ns) {
253         return psNS.read(trans, R_TEXT, new Object[]{ns});
254     }
255     
256     public Result<List<Data>> readID(AuthzTrans trans, String id) {
257         return psID.read(trans, R_TEXT, new Object[]{id});
258     }
259     
260     public Result<List<Data>> readIDBAth(AuthzTrans trans, String id) {
261         return psIDBath.read(trans, R_TEXT, new Object[] {id});
262     }
263
264     /**
265      * Log Modification statements to History
266      *
267      * @param modified        which CRUD action was done
268      * @param data            entity data that needs a log entry
269      * @param overrideMessage if this is specified, we use it rather than crafting a history message based on data
270      */
271     @Override
272     protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
273         boolean memo = override.length>0 && override[0]!=null;
274         boolean subject = override.length>1 && override[1]!=null;
275
276         HistoryDAO.Data hd = HistoryDAO.newInitedData();
277         hd.user = trans.user();
278         hd.action = modified.name();
279         hd.target = TABLE;
280         hd.subject = subject?override[1]: data.id;
281         hd.memo = memo
282                 ? String.format("%s by %s", override[0], hd.user)
283                 : (modified.name() + "d credential for " + data.id);
284         String spacer = ": ";
285         if(data.notes!=null) {
286             hd.memo+=spacer + data.notes;
287             spacer = ", ";
288         }
289
290         if(data.tag!=null) {
291             hd.memo+=spacer + data.tag;
292         }
293
294         // Detail?
295            if (modified==CRUD.delete) {
296                     try {
297                         hd.reconstruct = data.bytify();
298                     } catch (IOException e) {
299                         trans.error().log(e,"Could not serialize CredDAO.Data");
300                     }
301                 }
302
303         if (historyDAO.create(trans, hd).status!=Status.OK) {
304             trans.error().log("Cannot log to History");
305         }
306         if (infoDAO.touch(trans, TABLE,data.invalidate(cache)).status!=Status.OK) {
307             trans.error().log("Cannot touch Cred");
308         }
309     }
310 }