Collection syntax change because of Sonar
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / cass / PermDAO.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.auth.dao.cass;
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.DataInputStream;
26 import java.io.DataOutputStream;
27 import java.io.IOException;
28 import java.nio.ByteBuffer;
29 import java.util.HashSet;
30 import java.util.List;
31 import java.util.Set;
32
33 import org.onap.aaf.auth.dao.Bytification;
34 import org.onap.aaf.auth.dao.Cached;
35 import org.onap.aaf.auth.dao.CassAccess;
36 import org.onap.aaf.auth.dao.CassDAOImpl;
37 import org.onap.aaf.auth.dao.DAOException;
38 import org.onap.aaf.auth.dao.Loader;
39 import org.onap.aaf.auth.dao.Streamer;
40 import org.onap.aaf.auth.dao.hl.Question;
41 import org.onap.aaf.auth.env.AuthzTrans;
42 import org.onap.aaf.auth.layer.Result;
43 import org.onap.aaf.misc.env.APIException;
44 import org.onap.aaf.misc.env.util.Split;
45
46 import com.datastax.driver.core.Cluster;
47 import com.datastax.driver.core.Row;
48 import com.datastax.driver.core.exceptions.DriverException;
49
50 public class PermDAO extends CassDAOImpl<AuthzTrans,PermDAO.Data> {
51
52         public static final String TABLE = "perm";
53
54     public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F
55         private static final String STAR = "*";
56         
57         private final HistoryDAO historyDAO;
58         private final CacheInfoDAO infoDAO;
59         
60         private PSInfo psNS, psChildren, psByType;
61
62         public PermDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
63                 super(trans, PermDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
64                 init(trans);
65                 historyDAO = new HistoryDAO(trans, this);
66                 infoDAO = new CacheInfoDAO(trans,this);
67         }
68
69         public PermDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO ciDAO) {
70                 super(trans, PermDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));
71                 historyDAO = hDAO;
72                 infoDAO=ciDAO;
73                 init(trans);
74         }
75
76
77         private static final int KEYLIMIT = 4;
78         public static class Data extends CacheableData implements Bytification {
79                 public String           ns;
80                 public String           type;
81                 public String           instance;
82                 public String           action;
83                 public Set<String>  roles; 
84                 public String           description;
85
86                 public Data() {}
87                 
88                 public Data(NsSplit nss, String instance, String action) {
89                         ns = nss.ns;
90                         type = nss.name;
91                         this.instance = instance;
92                         this.action = action;
93                 }
94
95                 public String fullType() {
96                         return ns + '.' + type;
97                 }
98                 
99                 public String fullPerm() {
100                         return ns + '.' + type + '|' + instance + '|' + action;
101                 }
102
103                 public String encode() {
104                         return ns + '|' + type + '|' + instance + '|' + action;
105                 }
106                 
107                 /**
108                  * Decode Perm String, including breaking into appropriate Namespace
109                  * 
110                  * @param trans
111                  * @param q
112                  * @param p
113                  * @return
114                  */
115                 public static Result<Data> decode(AuthzTrans trans, Question q, String p) {
116                         String[] ss = Split.splitTrim('|', p,4);
117                         if(ss[2]==null) {
118                                 return Result.err(Status.ERR_BadData,"Perm Encodings must be separated by '|'");
119                         }
120                         Data data = new Data();
121                         if(ss[3]==null) { // older 3 part encoding must be evaluated for NS
122                                 Result<NsSplit> nss = q.deriveNsSplit(trans, ss[0]);
123                                 if(nss.notOK()) {
124                                         return Result.err(nss);
125                                 }
126                                 data.ns=nss.value.ns;
127                                 data.type=nss.value.name;
128                                 data.instance=ss[1];
129                                 data.action=ss[2];
130                         } else { // new 4 part encoding
131                                 data.ns=ss[0];
132                                 data.type=ss[1];
133                                 data.instance=ss[2];
134                                 data.action=ss[3];
135                         }
136                         return Result.ok(data);
137                 }
138
139                 /**
140                  * Decode Perm String, including breaking into appropriate Namespace
141                  * 
142                  * @param trans
143                  * @param q
144                  * @param p
145                  * @return
146                  */
147                 public static Result<String[]> decodeToArray(AuthzTrans trans, Question q, String p) {
148                         String[] ss = Split.splitTrim('|', p,4);
149                         if(ss[2]==null) {
150                                 return Result.err(Status.ERR_BadData,"Perm Encodings must be separated by '|'");
151                         }
152                         
153                         if(ss[3]==null) { // older 3 part encoding must be evaluated for NS
154                                 ss[3] = ss[2];
155                                 ss[2] = ss[1];
156                                 Result<NsSplit> nss = q.deriveNsSplit(trans, ss[0]);
157                                 if(nss.notOK()) {
158                                         return Result.err(nss);
159                                 }
160                                 ss[1] = nss.value.name;
161                                 ss[0] = nss.value.ns;
162                         }
163                         return Result.ok(ss);
164                 }
165
166                 public static Data create(NsDAO.Data ns, String name) {
167                         NsSplit nss = new NsSplit(ns,name);
168                         Data rv = new Data();
169                         rv.ns = nss.ns;
170                         String[] s = nss.name.split("\\|");
171                         switch(s.length) {
172                                 case 3:
173                                         rv.type=s[0];
174                                         rv.instance=s[1];
175                                         rv.action=s[2];
176                                         break;
177                                 case 2:
178                                         rv.type=s[0];
179                                         rv.instance=s[1];
180                                         rv.action=STAR;
181                                         break;
182                                 default:
183                                         rv.type=s[0];
184                                         rv.instance = STAR;
185                                         rv.action = STAR;
186                         }
187                         return rv;
188                 }
189                 
190                 public static Data create(AuthzTrans trans, Question q, String name) {
191                         String[] s = name.split("\\|");
192                         Result<NsSplit> rdns = q.deriveNsSplit(trans, s[0]);
193                         Data rv = new PermDAO.Data();
194                         if(rdns.isOKhasData()) {
195                                 switch(s.length) {
196                                         case 3:
197                                                 rv.type=s[1];
198                                                 rv.instance=s[2];
199                                                 rv.action=s[3];
200                                                 break;
201                                         case 2:
202                                                 rv.type=s[1];
203                                                 rv.instance=s[2];
204                                                 rv.action=STAR;
205                                                 break;
206                                         default:
207                                                 rv.type=s[1];
208                                                 rv.instance = STAR;
209                                                 rv.action = STAR;
210                                 }
211                         }
212                         return rv;
213                 }
214                 
215         ////////////////////////////////////////
216         // Getters
217         public Set<String> roles(boolean mutable) {
218             if (roles == null) {
219                 roles = new HashSet<>();
220             } else if (mutable && !(roles instanceof HashSet)) {
221                 roles = new HashSet<>(roles);
222             }
223             return roles;
224         }
225
226                 @Override
227                 public int[] invalidate(Cached<?,?> cache) {
228                         return new int[] {
229                                 seg(cache,ns),
230                                 seg(cache,ns,type),
231                                 seg(cache,ns,type,STAR),
232                                 seg(cache,ns,type,instance,action)
233                         };
234                 }
235
236                 @Override
237                 public ByteBuffer bytify() throws IOException {
238                         ByteArrayOutputStream baos = new ByteArrayOutputStream();
239                         PermLoader.deflt.marshal(this, new DataOutputStream(baos));
240                         return ByteBuffer.wrap(baos.toByteArray());
241                 }
242                 
243                 @Override
244                 public void reconstitute(ByteBuffer bb) throws IOException {
245                         PermLoader.deflt.unmarshal(this, toDIS(bb));
246                 }
247
248                 @Override
249                 public String toString() {
250                         return encode();
251                 }
252         }
253         
254         private static class PermLoader extends Loader<Data> implements Streamer<Data> {
255                 public static final int MAGIC=283939453;
256         public static final int VERSION=1;
257         public static final int BUFF_SIZE=96;
258
259         public static final PermLoader deflt = new PermLoader(KEYLIMIT);
260         
261                 public PermLoader(int keylimit) {
262                         super(keylimit);
263                 }
264                 
265                 @Override
266                 public Data load(Data data, Row row) {
267                         // Int more efficient Match "fields" string
268                         data.ns = row.getString(0);
269                         data.type = row.getString(1);
270                         data.instance = row.getString(2);
271                         data.action = row.getString(3);
272                         data.roles = row.getSet(4,String.class);
273                         data.description = row.getString(5);
274                         return data;
275                 }
276
277                 @Override
278                 protected void key(Data data, int _idx, Object[] obj) {
279                         int idx = _idx;
280                         obj[idx]=data.ns;
281                         obj[++idx]=data.type;
282                         obj[++idx]=data.instance;
283                         obj[++idx]=data.action;
284                 }
285
286                 @Override
287                 protected void body(Data data, int _idx, Object[] obj) {
288                         int idx = _idx;
289                         obj[idx]=data.roles;
290                         obj[++idx]=data.description;
291                 }
292
293                 @Override
294                 public void marshal(Data data, DataOutputStream os) throws IOException {
295                         writeHeader(os,MAGIC,VERSION);
296                         writeString(os, data.ns);
297                         writeString(os, data.type);
298                         writeString(os, data.instance);
299                         writeString(os, data.action);
300                         writeStringSet(os, data.roles);
301                         writeString(os, data.description);
302                 }
303
304                 @Override
305                 public void unmarshal(Data data, DataInputStream is) throws IOException {
306                         /*int version = */readHeader(is,MAGIC,VERSION);
307                         // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields
308                         byte[] buff = new byte[BUFF_SIZE];
309                         data.ns = readString(is, buff);
310                         data.type = readString(is,buff);
311                         data.instance = readString(is,buff);
312                         data.action = readString(is,buff);
313                         data.roles = readStringSet(is,buff);
314                         data.description = readString(is,buff);
315                 }
316         }
317         
318         private void init(AuthzTrans trans) {
319                 // the 3 is the number of key fields
320                 String[] helpers = setCRUD(trans, TABLE, Data.class, PermLoader.deflt);
321                 
322                 // Other SELECT style statements... match with a local Method
323                 psByType = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE + 
324                                 " WHERE ns = ? AND type = ?", new PermLoader(2) {
325                         @Override
326                         protected void key(Data data, int idx, Object[] obj) {
327                                 obj[idx]=data.type;
328                         }
329                 },readConsistency);
330                 
331                 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +
332                                 " WHERE ns = ?", new PermLoader(1),readConsistency);
333                                 
334                 psChildren = new PSInfo(trans, SELECT_SP +  helpers[FIELD_COMMAS] +  " FROM " + TABLE + 
335                                 " WHERE ns=? AND type > ? AND type < ?", 
336                                 new PermLoader(3) {
337                         @Override
338                         protected void key(Data data, int _idx, Object[] obj) {
339                                 int idx = _idx;
340                                 obj[idx] = data.ns;
341                                 obj[++idx]=data.type + DOT;
342                                 obj[++idx]=data.type + DOT_PLUS_ONE;
343                         }
344                 },readConsistency);
345
346         }
347
348
349         /**
350          * Add a single Permission to the Role's Permission Collection
351          * 
352          * @param trans
353          * @param roleFullName
354          * @param perm
355          * @param type
356          * @param action
357          * @return
358          */
359         public Result<Void> addRole(AuthzTrans trans, PermDAO.Data perm, String roleFullName) {
360                 // Note: Prepared Statements for Collection updates aren't supported
361                 //ResultSet rv =
362                 try {
363                         getSession(trans).execute(UPDATE_SP + TABLE + " SET roles = roles + {'" + roleFullName + "'} " +
364                                 "WHERE " +
365                                         "ns = '" + perm.ns + "' AND " +
366                                         "type = '" + perm.type + "' AND " +
367                                         "instance = '" + perm.instance + "' AND " +
368                                         "action = '" + perm.action + "';"
369                                         );
370                 } catch (DriverException | APIException | IOException e) {
371                         reportPerhapsReset(trans,e);
372                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
373                 }
374
375                 wasModified(trans, CRUD.update, perm, "Added role " + roleFullName + " to perm " +
376                                 perm.ns + '.' + perm.type + '|' + perm.instance + '|' + perm.action);
377                 return Result.ok();
378         }
379
380         /**
381          * Remove a single Permission from the Role's Permission Collection
382          * @param trans
383          * @param roleFullName
384          * @param perm
385          * @param type
386          * @param action
387          * @return
388          */
389         public Result<Void> delRole(AuthzTrans trans, PermDAO.Data perm, String roleFullName) {
390                 // Note: Prepared Statements for Collection updates aren't supported
391                 //ResultSet rv =
392                 try {
393                         getSession(trans).execute(UPDATE_SP + TABLE + " SET roles = roles - {'" + roleFullName + "'} " +
394                                 "WHERE " +
395                                         "ns = '" + perm.ns + "' AND " +
396                                         "type = '" + perm.type + "' AND " +
397                                         "instance = '" + perm.instance + "' AND " +
398                                         "action = '" + perm.action + "';"
399                                         );
400                 } catch (DriverException | APIException | IOException e) {
401                         reportPerhapsReset(trans,e);
402                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
403                 }
404
405                 //TODO how can we tell when it doesn't?
406                 wasModified(trans, CRUD.update, perm, "Removed role " + roleFullName + " from perm " +
407                                 perm.ns + '.' + perm.type + '|' + perm.instance + '|' + perm.action);
408                 return Result.ok();
409         }
410
411
412         
413         /**
414          * Additional method: 
415          *              Select all Permissions by Name
416          * 
417          * @param name
418          * @return
419          * @throws DAOException
420          */
421         public Result<List<Data>> readByType(AuthzTrans trans, String ns, String type) {
422                 return psByType.read(trans, R_TEXT, new Object[]{ns, type});
423         }
424         
425         public Result<List<Data>> readChildren(AuthzTrans trans, String ns, String type) {
426                 return psChildren.read(trans, R_TEXT, new Object[]{ns, type+DOT, type + DOT_PLUS_ONE});
427         }
428
429         public Result<List<Data>> readNS(AuthzTrans trans, String ns) {
430                 return psNS.read(trans, R_TEXT, new Object[]{ns});
431         }
432
433         /**
434          * Add description to this permission
435          * 
436          * @param trans
437          * @param ns
438          * @param type
439          * @param instance
440          * @param action
441          * @param description
442          * @return
443          */
444         public Result<Void> addDescription(AuthzTrans trans, String ns, String type,
445                         String instance, String action, String description) {
446                 try {
447                         getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '" 
448                                 + description + "' WHERE ns = '" + ns + "' AND type = '" + type + "'"
449                                 + "AND instance = '" + instance + "' AND action = '" + action + "';");
450                 } catch (DriverException | APIException | IOException e) {
451                         reportPerhapsReset(trans,e);
452                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);
453                 }
454
455                 Data data = new Data();
456                 data.ns=ns;
457                 data.type=type;
458                 data.instance=instance;
459                 data.action=action;
460                 wasModified(trans, CRUD.update, data, "Added description " + description + " to permission " 
461                                 + data.encode(), null );
462                 return Result.ok();
463         }
464         
465         /**
466          * Log Modification statements to History
467          */
468         @Override
469         protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {
470         boolean memo = override.length>0 && override[0]!=null;
471         boolean subject = override.length>1 && override[1]!=null;
472
473                 // Need to update history
474                 HistoryDAO.Data hd = HistoryDAO.newInitedData();
475                 hd.user = trans.user();
476                 hd.action = modified.name();
477                 hd.target = TABLE;
478                 hd.subject = subject ? override[1] : data.fullType();
479                 if (memo) {
480             hd.memo = String.format("%s", override[0]);
481         } else {
482             hd.memo = String.format("%sd %s|%s|%s", modified.name(),data.fullType(),data.instance,data.action);
483         }
484                 
485                 if(modified==CRUD.delete) {
486                         try {
487                                 hd.reconstruct = data.bytify();
488                         } catch (IOException e) {
489                                 trans.error().log(e,"Could not serialize PermDAO.Data");
490                         }
491                 }
492                 
493         if(historyDAO.create(trans, hd).status!=Status.OK) {
494                 trans.error().log("Cannot log to History");
495         }
496         if(infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {
497                 trans.error().log("Cannot touch CacheInfo");
498         }
499         }
500 }
501