AT&T 2.0.19 Code drop, stage 4
[aaf/authz.git] / authz-cass / src / main / java / org / onap / aaf / dao / aaf / cass / RoleDAO.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.Loader;\r
41 import org.onap.aaf.dao.Streamer;\r
42 import org.onap.aaf.dao.aaf.hl.Question;\r
43 \r
44 import org.onap.aaf.inno.env.APIException;\r
45 import org.onap.aaf.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 RoleDAO extends CassDAOImpl<AuthzTrans,RoleDAO.Data> {\r
51 \r
52         public static final String TABLE = "role";\r
53     public static final int CACHE_SEG = 0x40; // yields segment 0x0-0x3F\r
54     \r
55         private final HistoryDAO historyDAO;\r
56         private final CacheInfoDAO infoDAO;\r
57 \r
58         private PSInfo psChildren, psNS, psName;\r
59 \r
60         public RoleDAO(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {\r
61                 super(trans, RoleDAO.class.getSimpleName(),cluster,keyspace,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));\r
62         // Set up sub-DAOs\r
63         historyDAO = new HistoryDAO(trans, this);\r
64                 infoDAO = new CacheInfoDAO(trans,this);\r
65                 init(trans);\r
66         }\r
67 \r
68         public RoleDAO(AuthzTrans trans, HistoryDAO hDAO, CacheInfoDAO ciDAO) {\r
69                 super(trans, RoleDAO.class.getSimpleName(),hDAO,Data.class,TABLE, readConsistency(trans,TABLE), writeConsistency(trans,TABLE));\r
70                 historyDAO = hDAO;\r
71                 infoDAO = ciDAO;\r
72                 init(trans);\r
73         }\r
74 \r
75 \r
76     //////////////////////////////////////////\r
77     // Data Definition, matches Cassandra DM\r
78     //////////////////////////////////////////\r
79     private static final int KEYLIMIT = 2;\r
80     /**\r
81      * Data class that matches the Cassandra Table "role"\r
82      */\r
83         public static class Data extends CacheableData implements Bytification {\r
84         public String           ns;\r
85                 public String           name;\r
86                 public Set<String>  perms;\r
87                 public String           description;\r
88 \r
89         ////////////////////////////////////////\r
90         // Getters\r
91                 public Set<String> perms(boolean mutable) {\r
92                         if (perms == null) {\r
93                                 perms = new HashSet<String>();\r
94                         } else if (mutable && !(perms instanceof HashSet)) {\r
95                                 perms = new HashSet<String>(perms);\r
96                         }\r
97                         return perms;\r
98                 }\r
99                 \r
100                 public static Data create(NsDAO.Data ns, String name) {\r
101                         NsSplit nss = new NsSplit(ns,name);             \r
102                         RoleDAO.Data rv = new Data();\r
103                         rv.ns = nss.ns;\r
104                         rv.name=nss.name;\r
105                         return rv;\r
106                 }\r
107                 \r
108                 public String fullName() {\r
109                         return ns + '.' + name;\r
110                 }\r
111                 \r
112                 public String encode() {\r
113                         return ns + '|' + name;\r
114                 }\r
115                 \r
116                 /**\r
117                  * Decode Perm String, including breaking into appropriate Namespace\r
118                  * \r
119                  * @param trans\r
120                  * @param q\r
121                  * @param r\r
122                  * @return\r
123                  */\r
124                 public static Result<Data> decode(AuthzTrans trans, Question q, String r) {\r
125                         String[] ss = Split.splitTrim('|', r,2);\r
126                         Data data = new Data();\r
127                         if(ss[1]==null) { // older 1 part encoding must be evaluated for NS\r
128                                 Result<NsSplit> nss = q.deriveNsSplit(trans, ss[0]);\r
129                                 if(nss.notOK()) {\r
130                                         return Result.err(nss);\r
131                                 }\r
132                                 data.ns=nss.value.ns;\r
133                                 data.name=nss.value.name;\r
134                         } else { // new 4 part encoding\r
135                                 data.ns=ss[0];\r
136                                 data.name=ss[1];\r
137                         }\r
138                         return Result.ok(data);\r
139                 }\r
140 \r
141                 /**\r
142                  * Decode from UserRole Data\r
143                  * @param urdd\r
144                  * @return\r
145                  */\r
146                 public static RoleDAO.Data decode(UserRoleDAO.Data urdd) {\r
147                         RoleDAO.Data rd = new RoleDAO.Data();\r
148                         rd.ns = urdd.ns;\r
149                         rd.name = urdd.rname;\r
150                         return rd;\r
151                 }\r
152 \r
153 \r
154                 /**\r
155                  * Decode Perm String, including breaking into appropriate Namespace\r
156                  * \r
157                  * @param trans\r
158                  * @param q\r
159                  * @param p\r
160                  * @return\r
161                  */\r
162                 public static Result<String[]> decodeToArray(AuthzTrans trans, Question q, String p) {\r
163                         String[] ss = Split.splitTrim('|', p,2);\r
164                         if(ss[1]==null) { // older 1 part encoding must be evaluated for NS\r
165                                 Result<NsSplit> nss = q.deriveNsSplit(trans, ss[0]);\r
166                                 if(nss.notOK()) {\r
167                                         return Result.err(nss);\r
168                                 }\r
169                                 ss[0] = nss.value.ns;\r
170                                 ss[1] = nss.value.name;\r
171                         }\r
172                         return Result.ok(ss);\r
173                 }\r
174                 \r
175                 @Override\r
176                 public int[] invalidate(Cached<?,?> cache) {\r
177                         return new int[] {\r
178                                 seg(cache,ns,name),\r
179                                 seg(cache,ns),\r
180                                 seg(cache,name),\r
181                         };\r
182                 }\r
183 \r
184                 @Override\r
185                 public ByteBuffer bytify() throws IOException {\r
186                         ByteArrayOutputStream baos = new ByteArrayOutputStream();\r
187                         RoleLoader.deflt.marshal(this,new DataOutputStream(baos));\r
188                         return ByteBuffer.wrap(baos.toByteArray());\r
189                 }\r
190                 \r
191                 @Override\r
192                 public void reconstitute(ByteBuffer bb) throws IOException {\r
193                         RoleLoader.deflt.unmarshal(this, toDIS(bb));\r
194                 }\r
195 \r
196                 @Override\r
197                 public String toString() {\r
198                         return ns + '.' + name;\r
199                 }\r
200     }\r
201 \r
202     private static class RoleLoader extends Loader<Data> implements Streamer<Data> {\r
203                 public static final int MAGIC=923577343;\r
204         public static final int VERSION=1;\r
205         public static final int BUFF_SIZE=96;\r
206 \r
207         public static final RoleLoader deflt = new RoleLoader(KEYLIMIT);\r
208         \r
209                 public RoleLoader(int keylimit) {\r
210                         super(keylimit);\r
211                 }\r
212                 \r
213                 @Override\r
214                 public Data load(Data data, Row row) {\r
215                         // Int more efficient\r
216                         data.ns = row.getString(0);\r
217                         data.name = row.getString(1);\r
218                         data.perms = row.getSet(2,String.class);\r
219                         data.description = row.getString(3);\r
220                         return data;\r
221                 }\r
222 \r
223                 @Override\r
224                 protected void key(Data data, int _idx, Object[] obj) {\r
225                         int idx = _idx;\r
226                         obj[idx]=data.ns;\r
227                         obj[++idx]=data.name;\r
228                 }\r
229 \r
230                 @Override\r
231                 protected void body(Data data, int _idx, Object[] obj) {\r
232                         int idx = _idx;\r
233                         obj[idx]=data.perms;\r
234                         obj[++idx]=data.description;\r
235                 }\r
236 \r
237                 @Override\r
238                 public void marshal(Data data, DataOutputStream os) throws IOException {\r
239                         writeHeader(os,MAGIC,VERSION);\r
240                         writeString(os, data.ns);\r
241                         writeString(os, data.name);\r
242                         writeStringSet(os,data.perms);\r
243                         writeString(os, data.description);\r
244                 }\r
245 \r
246                 @Override\r
247                 public void unmarshal(Data data, DataInputStream is) throws IOException {\r
248                         /*int version = */readHeader(is,MAGIC,VERSION);\r
249                         // If Version Changes between Production runs, you'll need to do a switch Statement, and adequately read in fields\r
250                         byte[] buff = new byte[BUFF_SIZE];\r
251                         data.ns = readString(is, buff);\r
252                         data.name = readString(is,buff);\r
253                         data.perms = readStringSet(is,buff);\r
254                         data.description = readString(is,buff);\r
255                 }\r
256     };\r
257 \r
258         private void init(AuthzTrans trans) {\r
259                 String[] helpers = setCRUD(trans, TABLE, Data.class, RoleLoader.deflt);\r
260                 \r
261                 psNS = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +\r
262                                 " WHERE ns = ?", new RoleLoader(1),readConsistency);\r
263 \r
264                 psName = new PSInfo(trans, SELECT_SP + helpers[FIELD_COMMAS] + " FROM " + TABLE +\r
265                                 " WHERE name = ?", new RoleLoader(1),readConsistency);\r
266 \r
267                 psChildren = new PSInfo(trans, SELECT_SP +  helpers[FIELD_COMMAS] +  " FROM " + TABLE + \r
268                                 " WHERE ns=? AND name > ? AND name < ?", \r
269                                 new RoleLoader(3) {\r
270                         @Override\r
271                         protected void key(Data data, int _idx, Object[] obj) {\r
272                                 int idx = _idx;\r
273                                 obj[idx] = data.ns;\r
274                                 obj[++idx]=data.name + DOT;\r
275                                 obj[++idx]=data.name + DOT_PLUS_ONE;\r
276                         }\r
277                 },readConsistency);\r
278                 \r
279         }\r
280 \r
281         public Result<List<Data>> readNS(AuthzTrans trans, String ns) {\r
282                 return psNS.read(trans, R_TEXT + " NS " + ns, new Object[]{ns});\r
283         }\r
284 \r
285         public Result<List<Data>> readName(AuthzTrans trans, String name) {\r
286                 return psName.read(trans, R_TEXT + name, new Object[]{name});\r
287         }\r
288 \r
289         public Result<List<Data>> readChildren(AuthzTrans trans, String ns, String role) {\r
290                 if(role.length()==0 || "*".equals(role)) {\r
291                         return psChildren.read(trans, R_TEXT, new Object[]{ns, FIRST_CHAR, LAST_CHAR}); \r
292                 } else {\r
293                         return psChildren.read(trans, R_TEXT, new Object[]{ns, role+DOT, role+DOT_PLUS_ONE});\r
294                 }\r
295         }\r
296 \r
297         /**\r
298          * Add a single Permission to the Role's Permission Collection\r
299          * \r
300          * @param trans\r
301          * @param role\r
302          * @param perm\r
303          * @param type\r
304          * @param action\r
305          * @return\r
306          */\r
307         public Result<Void> addPerm(AuthzTrans trans, RoleDAO.Data role, PermDAO.Data perm) {\r
308                 // Note: Prepared Statements for Collection updates aren't supported\r
309                 String pencode = perm.encode();\r
310                 try {\r
311                         getSession(trans).execute(UPDATE_SP + TABLE + " SET perms = perms + {'" + \r
312                                 pencode + "'} WHERE " +\r
313                                 "ns = '" + role.ns + "' AND name = '" + role.name + "';");\r
314                 } catch (DriverException | APIException | IOException e) {\r
315                         reportPerhapsReset(trans,e);\r
316                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);\r
317                 }\r
318 \r
319                 wasModified(trans, CRUD.update, role, "Added permission " + pencode + " to role " + role.fullName());\r
320                 return Result.ok();\r
321         }\r
322 \r
323         /**\r
324          * Remove a single Permission from the Role's Permission Collection\r
325          * @param trans\r
326          * @param role\r
327          * @param perm\r
328          * @param type\r
329          * @param action\r
330          * @return\r
331          */\r
332         public Result<Void> delPerm(AuthzTrans trans, RoleDAO.Data role, PermDAO.Data perm) {\r
333                 // Note: Prepared Statements for Collection updates aren't supported\r
334 \r
335                 String pencode = perm.encode();\r
336                 \r
337                 //ResultSet rv =\r
338                 try {\r
339                         getSession(trans).execute(UPDATE_SP + TABLE + " SET perms = perms - {'" + \r
340                                 pencode + "'} WHERE " +\r
341                                 "ns = '" + role.ns + "' AND name = '" + role.name + "';");\r
342                 } catch (DriverException | APIException | IOException e) {\r
343                         reportPerhapsReset(trans,e);\r
344                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);\r
345                 }\r
346 \r
347                 //TODO how can we tell when it doesn't?\r
348                 wasModified(trans, CRUD.update, role, "Removed permission " + pencode + " from role " + role.fullName() );\r
349                 return Result.ok();\r
350         }\r
351         \r
352         /**\r
353          * Add description to role\r
354          * \r
355          * @param trans\r
356          * @param ns\r
357          * @param name\r
358          * @param description\r
359          * @return\r
360          */\r
361         public Result<Void> addDescription(AuthzTrans trans, String ns, String name, String description) {\r
362                 try {\r
363                         getSession(trans).execute(UPDATE_SP + TABLE + " SET description = '" \r
364                                 + description + "' WHERE ns = '" + ns + "' AND name = '" + name + "';");\r
365                 } catch (DriverException | APIException | IOException e) {\r
366                         reportPerhapsReset(trans,e);\r
367                         return Result.err(Result.ERR_Backend, CassAccess.ERR_ACCESS_MSG);\r
368                 }\r
369 \r
370                 Data data = new Data();\r
371                 data.ns=ns;\r
372                 data.name=name;\r
373                 wasModified(trans, CRUD.update, data, "Added description " + description + " to role " + data.fullName(), null );\r
374                 return Result.ok();\r
375         }\r
376         \r
377         \r
378     /**\r
379      * Log Modification statements to History\r
380      * @param modified           which CRUD action was done\r
381      * @param data               entity data that needs a log entry\r
382      * @param overrideMessage    if this is specified, we use it rather than crafting a history message based on data\r
383      */\r
384     @Override\r
385     protected void wasModified(AuthzTrans trans, CRUD modified, Data data, String ... override) {\r
386         boolean memo = override.length>0 && override[0]!=null;\r
387         boolean subject = override.length>1 && override[1]!=null;\r
388 \r
389         HistoryDAO.Data hd = HistoryDAO.newInitedData();\r
390         hd.user = trans.user();\r
391         hd.action = modified.name();\r
392         hd.target = TABLE;\r
393         hd.subject = subject ? override[1] : data.fullName();\r
394         hd.memo = memo ? override[0] : (data.fullName() + " was "  + modified.name() + 'd' );\r
395                 if(modified==CRUD.delete) {\r
396                         try {\r
397                                 hd.reconstruct = data.bytify();\r
398                         } catch (IOException e) {\r
399                                 trans.error().log(e,"Could not serialize RoleDAO.Data");\r
400                         }\r
401                 }\r
402 \r
403         if(historyDAO.create(trans, hd).status!=Status.OK) {\r
404                 trans.error().log("Cannot log to History");\r
405         }\r
406         if(infoDAO.touch(trans, TABLE,data.invalidate(cache)).notOK()) {\r
407                 trans.error().log("Cannot touch CacheInfo for Role");\r
408         }\r
409     }\r
410 \r
411     \r
412 }\r