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