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