Merge "Refactor Batch: Remove"
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / hl / Question.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.hl;
23
24 import java.io.IOException;
25 import java.nio.ByteBuffer;
26 import java.security.NoSuchAlgorithmException;
27 import java.security.SecureRandom;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.Date;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Random;
36 import java.util.Set;
37 import java.util.TreeSet;
38
39 import org.onap.aaf.auth.common.Define;
40 import org.onap.aaf.auth.dao.AbsCassDAO;
41 import org.onap.aaf.auth.dao.CachedDAO;
42 import org.onap.aaf.auth.dao.DAOException;
43 import org.onap.aaf.auth.dao.cached.CachedCertDAO;
44 import org.onap.aaf.auth.dao.cached.CachedCredDAO;
45 import org.onap.aaf.auth.dao.cached.CachedNSDAO;
46 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
47 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
48 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
49 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
50 import org.onap.aaf.auth.dao.cass.CacheInfoDAO;
51 import org.onap.aaf.auth.dao.cass.CertDAO;
52 import org.onap.aaf.auth.dao.cass.CredDAO;
53 import org.onap.aaf.auth.dao.cass.CredDAO.Data;
54 import org.onap.aaf.auth.dao.cass.DelegateDAO;
55 import org.onap.aaf.auth.dao.cass.FutureDAO;
56 import org.onap.aaf.auth.dao.cass.HistoryDAO;
57 import org.onap.aaf.auth.dao.cass.LocateDAO;
58 import org.onap.aaf.auth.dao.cass.NsDAO;
59 import org.onap.aaf.auth.dao.cass.NsSplit;
60 import org.onap.aaf.auth.dao.cass.NsType;
61 import org.onap.aaf.auth.dao.cass.PermDAO;
62 import org.onap.aaf.auth.dao.cass.RoleDAO;
63 import org.onap.aaf.auth.dao.cass.Status;
64 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
65 import org.onap.aaf.auth.env.AuthzTrans;
66 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
67 import org.onap.aaf.auth.env.AuthzTransFilter;
68 import org.onap.aaf.auth.layer.Result;
69 import org.onap.aaf.auth.org.Organization;
70 import org.onap.aaf.cadi.Hash;
71 import org.onap.aaf.cadi.aaf.PermEval;
72 import org.onap.aaf.cadi.config.Config;
73 import org.onap.aaf.misc.env.APIException;
74 import org.onap.aaf.misc.env.Env;
75 import org.onap.aaf.misc.env.Slot;
76 import org.onap.aaf.misc.env.TimeTaken;
77 import org.onap.aaf.misc.env.util.Chrono;
78
79 import com.datastax.driver.core.Cluster;
80
81 /**
82  * Question HL DAO
83  * 
84  * A Data Access Combination Object which asks Security and other Questions
85  * 
86  * @author Jonathan
87  *
88  */
89 public class Question {
90
91     // DON'T CHANGE FROM lower Case!!!
92     public static enum Type {
93         ns, role, perm, cred
94     };
95
96     public static final String OWNER="owner";
97     public static final String ADMIN="admin";
98     public static final String DOT_OWNER=".owner";
99     public static final String DOT_ADMIN=".admin";
100     public static final String ACCESS = "access";
101
102     static final String ASTERIX = "*";
103
104     public static enum Access {
105         read, write, create
106     };
107
108     public static final String READ = Access.read.name();
109     public static final String WRITE = Access.write.name();
110     public static final String CREATE = Access.create.name();
111
112     public static final String ROLE = Type.role.name();
113     public static final String PERM = Type.perm.name();
114     public static final String NS = Type.ns.name();
115     public static final String CRED = Type.cred.name();
116     private static final String DELG = "delg";
117     public static final String ROOT_NS = Define.isInitialized() ? Define.ROOT_NS() : "undefined";
118     public static final String ATTRIB = "attrib";
119
120
121     public static final int MAX_SCOPE = 10;
122     public static final int APP_SCOPE = 3;
123     public static final int COMPANY_SCOPE = 2;
124     static Slot PERMS;
125
126     private static Set<String> specialLog = null;
127     public static final Random random = new SecureRandom();
128     private static long traceID = random.nextLong();
129     private static Slot specialLogSlot = null;
130     private static Slot transIDSlot = null;
131
132
133     public final HistoryDAO historyDAO;
134     public final CachedNSDAO nsDAO;
135     public final CachedRoleDAO roleDAO;
136     public final CachedPermDAO permDAO;
137     public final CachedUserRoleDAO userRoleDAO;
138     public final CachedCredDAO credDAO;
139     public final CachedCertDAO certDAO;
140     public final DelegateDAO delegateDAO;
141     public final FutureDAO futureDAO;
142     public final ApprovalDAO approvalDAO;
143     private final CacheInfoDAO cacheInfoDAO;
144     public final LocateDAO locateDAO;
145
146     public Question(AuthzTrans trans, Cluster cluster, String keyspace, boolean startClean) throws APIException, IOException {
147         PERMS = trans.slot("USER_PERMS");
148         trans.init().log("Instantiating DAOs");
149         long expiresIn = Long.parseLong(trans.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF));
150         historyDAO = new HistoryDAO(trans, cluster, keyspace);
151
152         // Deal with Cached Entries
153         cacheInfoDAO = new CacheInfoDAO(trans, historyDAO);
154
155         nsDAO = new CachedNSDAO(new NsDAO(trans, historyDAO, cacheInfoDAO),cacheInfoDAO, expiresIn);
156         permDAO = new CachedPermDAO(new PermDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
157         roleDAO = new CachedRoleDAO(new RoleDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
158         userRoleDAO = new CachedUserRoleDAO(new UserRoleDAO(trans, historyDAO,cacheInfoDAO), cacheInfoDAO, expiresIn);
159         credDAO = new CachedCredDAO(new CredDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
160         certDAO = new CachedCertDAO(new CertDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
161
162         locateDAO = new LocateDAO(trans,historyDAO);
163         futureDAO = new FutureDAO(trans, historyDAO);
164         delegateDAO = new DelegateDAO(trans, historyDAO);
165         approvalDAO = new ApprovalDAO(trans, historyDAO);
166
167         // Only want to aggressively cleanse User related Caches... The others,
168         // just normal refresh
169         if (startClean) {
170             CachedDAO.startCleansing(trans.env(), credDAO, userRoleDAO);
171             CachedDAO.startRefresh(trans.env(), cacheInfoDAO);
172         }
173         // Set a Timer to Check Caches to send messages for Caching changes
174         
175         if (specialLogSlot==null) {
176             specialLogSlot = trans.slot(AuthzTransFilter.SPECIAL_LOG_SLOT);
177         }
178         
179         if (transIDSlot==null) {
180             transIDSlot = trans.slot(AuthzTransFilter.TRANS_ID_SLOT);
181         }
182         
183         AbsCassDAO.primePSIs(trans);
184     }
185
186
187     public void close(AuthzTrans trans) {
188         historyDAO.close(trans);
189         cacheInfoDAO.close(trans);
190         nsDAO.close(trans);
191         permDAO.close(trans);
192         roleDAO.close(trans);
193         userRoleDAO.close(trans);
194         credDAO.close(trans);
195         certDAO.close(trans);
196         delegateDAO.close(trans);
197         futureDAO.close(trans);
198         approvalDAO.close(trans);
199     }
200
201     public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type,
202             String instance, String action) {
203         Result<NsDAO.Data> rnd = deriveNs(trans, type);
204         if (rnd.isOK()) {
205             return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
206                     instance, action));
207         } else {
208             return Result.err(rnd);
209         }
210     }
211
212     /**
213      * getPermsByUser
214      * 
215      * Because this call is frequently called internally, AND because we already
216      * look for it in the initial Call, we cache within the Transaction
217      * 
218      * @param trans
219      * @param user
220      * @return
221      */
222     public Result<List<PermDAO.Data>> getPermsByUser(AuthzTrans trans, String user, boolean lookup) {
223         return PermLookup.get(trans, this, user).getPerms(lookup);
224     }
225     
226     public Result<List<PermDAO.Data>> getPermsByUserFromRolesFilter(AuthzTrans trans, String user, String forUser) {
227         PermLookup plUser = PermLookup.get(trans, this, user);
228         Result<Set<String>> plPermNames = plUser.getPermNames();
229         if (plPermNames.notOK()) {
230             return Result.err(plPermNames);
231         }
232         
233         Set<String> nss;
234         if (forUser.equals(user)) {
235             nss = null;
236         } else {
237             // Setup a TreeSet to check on Namespaces to 
238             nss = new TreeSet<>();
239             PermLookup fUser = PermLookup.get(trans, this, forUser);
240             Result<Set<String>> forUpn = fUser.getPermNames();
241             if (forUpn.notOK()) {
242                 return Result.err(forUpn);
243             }
244             
245             for (String pn : forUpn.value) {
246                 Result<String[]> decoded = PermDAO.Data.decodeToArray(trans, this, pn);
247                 if (decoded.isOKhasData()) {
248                     nss.add(decoded.value[0]);
249                 } else {
250                     trans.error().log(pn,", derived from a Role, is invalid:",decoded.errorString());
251                 }
252             }
253         }
254
255         List<PermDAO.Data> rlpUser = new ArrayList<>();
256         Result<PermDAO.Data> rpdd;
257         PermDAO.Data pdd;
258         for (String pn : plPermNames.value) {
259             rpdd = PermDAO.Data.decode(trans, this, pn);
260             if (rpdd.isOKhasData()) {
261                 pdd=rpdd.value;
262                 if (nss==null || nss.contains(pdd.ns)) {
263                     rlpUser.add(pdd);
264                 }
265             } else {
266                 trans.error().log(pn,", derived from a Role, is invalid.  Run Data Cleanup:",rpdd.errorString());
267             }
268         }
269         return Result.ok(rlpUser); 
270     }
271
272     public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String perm) {
273         Result<NsSplit> nss = deriveNsSplit(trans, perm);
274         if (nss.notOK()) {
275             return Result.err(nss);
276         }
277         return permDAO.readByType(trans, nss.value.ns, nss.value.name);
278     }
279
280     public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans,
281             String type, String instance, String action) {
282         Result<NsSplit> nss = deriveNsSplit(trans, type);
283         if (nss.notOK()) {
284             return Result.err(nss);
285         }
286         return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
287     }
288
289     public Result<List<PermDAO.Data>> getPermsByRole(AuthzTrans trans, String role, boolean lookup) {
290         Result<NsSplit> nss = deriveNsSplit(trans, role);
291         if (nss.notOK()) {
292             return Result.err(nss);
293         }
294
295         Result<List<RoleDAO.Data>> rlrd = roleDAO.read(trans, nss.value.ns,
296                 nss.value.name);
297         if (rlrd.notOKorIsEmpty()) {
298             return Result.err(rlrd);
299         }
300         // Using Set to avoid duplicates
301         Set<String> permNames = new HashSet<>();
302         if (rlrd.isOKhasData()) {
303             for (RoleDAO.Data drr : rlrd.value) {
304                 permNames.addAll(drr.perms(false));
305             }
306         }
307
308         // Note: It should be ok for a Valid user to have no permissions -
309         // Jonathan 8/12/2013
310         List<PermDAO.Data> perms = new ArrayList<>();
311         for (String perm : permNames) {
312             Result<PermDAO.Data> pr = PermDAO.Data.decode(trans, this, perm);
313             if (pr.notOK()) {
314                 return Result.err(pr);
315             }
316
317             if (lookup) {
318                 Result<List<PermDAO.Data>> rlpd = permDAO.read(trans, pr.value);
319                 if (rlpd.isOKhasData()) {
320                     for (PermDAO.Data pData : rlpd.value) {
321                         perms.add(pData);
322                     }
323                 }
324             } else {
325                 perms.add(pr.value);
326             }
327         }
328
329         return Result.ok(perms);
330     }
331
332     public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans,
333             String role) {
334         Result<NsSplit> nss = deriveNsSplit(trans, role);
335         if (nss.notOK()) {
336             return Result.err(nss);
337         }
338         String r = nss.value.name;
339         if (r.endsWith(".*")) { // do children Search
340             return roleDAO.readChildren(trans, nss.value.ns,
341                     r.substring(0, r.length() - 2));
342         } else if (ASTERIX.equals(r)) {
343             return roleDAO.readChildren(trans, nss.value.ns, ASTERIX);
344         } else {
345             return roleDAO.read(trans, nss.value.ns, r);
346         }
347     }
348
349     /**
350      * Derive NS
351      * 
352      * Given a Child Namespace, figure out what the best Namespace parent is.
353      * 
354      * For instance, if in the NS table, the parent "org.osaaf" exists, but not
355      * "org.osaaf.child" or "org.osaaf.a.b.c", then passing in either
356      * "org.osaaf.child" or "org.osaaf.a.b.c" will return "org.osaaf"
357      * 
358      * Uses recursive search on Cached DAO data
359      * 
360      * @param trans
361      * @param child
362      * @return
363      */
364     public Result<NsDAO.Data> deriveNs(AuthzTrans trans, String child) {
365         Result<List<NsDAO.Data>> r = nsDAO.read(trans, child);
366         
367         if (r.isOKhasData()) {
368             return Result.ok(r.value.get(0));
369         } else {
370             int dot;
371             if (child==null) {
372                 return Result.err(Status.ERR_NsNotFound, "No Namespace");
373             } else {
374                 dot = child.lastIndexOf('.');
375             }
376             if (dot < 0) {
377                 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", child);
378             } else {
379                 return deriveNs(trans, child.substring(0, dot));
380             }
381         }
382     }
383
384     public Result<NsDAO.Data> deriveFirstNsForType(AuthzTrans trans, String str, NsType type) {
385         NsDAO.Data nsd;
386
387         for (String lookup = str;!".".equals(lookup) && lookup!=null;) {
388             Result<List<NsDAO.Data>> rld = nsDAO.read(trans, lookup);
389             if (rld.isOKhasData()) {
390                 nsd=rld.value.get(0);
391                 lookup = nsd.parent;
392                 if (type.type == nsd.type) {
393                     return Result.ok(nsd);
394                 } else {
395                     int dot = str.lastIndexOf('.');
396                     
397                     if (dot < 0) {
398                         return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
399                     } else {
400                         return deriveFirstNsForType(trans, str.substring(0, dot),type);
401                     }
402                 }
403             } else {
404                 int dot = str.lastIndexOf('.');
405                 
406                 if (dot < 0) {
407                     return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
408                 } else {
409                     return deriveFirstNsForType(trans, str.substring(0, dot),type);
410                 }
411             }
412         }
413         return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
414     }
415
416     public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
417         Result<NsDAO.Data> ndd = deriveNs(trans, child);
418         if (ndd.isOK()) {
419             NsSplit nss = new NsSplit(ndd.value, child);
420             if (nss.isOK()) {
421                 return Result.ok(nss);
422             } else {
423                 return Result.err(Status.ERR_NsNotFound,
424                         "Cannot split [%s] into valid namespace elements",
425                         child);
426             }
427         }
428         return Result.err(ndd);
429     }
430
431     /**
432      * Translate an ID into it's domain
433      * 
434      * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
435      * 
436      * @param id
437      * @return
438      */
439     public static String domain2ns(String id) {
440         int at = id.indexOf('@');
441         if (at >= 0) {
442             String[] domain = id.substring(at + 1).split("\\.");
443             StringBuilder ns = new StringBuilder(id.length());
444             boolean first = true;
445             for (int i = domain.length - 1; i >= 0; --i) {
446                 if (first) {
447                     first = false;
448                 } else {
449                     ns.append('.');
450                 }
451                 ns.append(domain[i]);
452             }
453             return ns.toString();
454         } else {
455             return "";
456         }
457
458     }
459
460     /**
461      * Validate Namespace of ID@Domain
462      * 
463      * Namespace is reverse order of Domain.
464      * 
465      * @param trans
466      * @param id
467      * @return
468      */
469     public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
470         // Take domain, reverse order, and check on NS
471         String ns;
472         if (id.indexOf('@')<0) { // it's already an ns, not an ID
473             ns = id;
474         } else {
475             ns = domain2ns(id);
476         }
477         if (ns.length() > 0) {
478             if (!trans.org().getDomain().equals(ns)) { 
479                 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
480                 if (rlnsd.isOKhasData()) {
481                     return Result.ok(rlnsd.value.get(0));
482                 }
483             }
484         }
485         return Result.err(Status.ERR_NsNotFound,
486                 "A Namespace is not available for %s", id);
487     }
488
489     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
490         // <ns>.access|:role:<role name>|<read|write>
491         String ns = ndd.name;
492         int last;
493         do {
494             if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
495                 return Result.ok(ndd);
496             }
497             if ((last = ns.lastIndexOf('.')) >= 0) {
498                 ns = ns.substring(0, last);
499             }
500         } while (last >= 0);
501         // com.att.aaf.ns|:<client ns>:ns|<access>
502         // AAF-724 - Make consistent response for May User", and not take the
503         // last check... too confusing.
504         Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + ndd.name + ":ns", access.name());
505         if (rv.isOK()) {
506             return rv;
507         } else if (rv.status==Result.ERR_Backend) {
508             return Result.err(rv);
509         } else {
510             return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
511                     user, access.name(), ndd.name);
512         }
513     }
514
515     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
516         Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
517         if (rnsd.isOK()) {
518             return mayUser(trans, user, rnsd.value, rdd, access);
519         }
520         return rnsd;
521     }
522
523     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
524         // 1) Is User in the Role?
525         Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
526         if (rurd.isOKhasData()) {
527             return Result.ok(ndd);
528         }
529
530         String roleInst = ":role:" + rdd.name;
531         // <ns>.access|:role:<role name>|<read|write>
532         String ns = rdd.ns;
533         int last;
534         do {
535             if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
536                 return Result.ok(ndd);
537             }
538             if ((last = ns.lastIndexOf('.')) >= 0) {
539                 ns = ns.substring(0, last);
540             }
541         } while (last >= 0);
542
543         // Check if Access by Global Role perm
544         // com.att.aaf.ns|:<client ns>:role:name|<access>
545         Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
546                 + rdd.ns + roleInst, access.name());
547         if (rnsd.isOK()) {
548             return rnsd;
549         } else if (rnsd.status==Result.ERR_Backend) {
550             return Result.err(rnsd);
551         }
552
553         // Check if Access to Whole NS
554         // AAF-724 - Make consistent response for May User", and not take the
555         // last check... too confusing.
556         Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, 
557                 ":" + rdd.ns + ":ns", access.name());
558         if (rv.isOK()) {
559             return rv;
560         } else if (rnsd.status==Result.ERR_Backend) {
561             return Result.err(rnsd);
562         } else {
563             return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
564                     user, access.name(), rdd.fullName());
565         }
566
567     }
568
569     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
570         Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
571         if (rnsd.isOK()) {
572             return mayUser(trans, user, rnsd.value, pdd, access);
573         }
574         return rnsd;
575     }
576
577     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
578         if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
579             return Result.ok(ndd);
580         }
581         String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
582         // <ns>.access|:role:<role name>|<read|write>
583         String ns = ndd.name;
584         int last;
585         do {
586             if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
587                 return Result.ok(ndd);
588             }
589             if ((last = ns.lastIndexOf('.')) >= 0) {
590                 ns = ns.substring(0, last);
591             }
592         } while (last >= 0);
593
594         // Check if Access by NS perm
595         // com.att.aaf.ns|:<client ns>:role:name|<access>
596         Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
597         if (rnsd.isOK()) {
598             return rnsd;
599         } else if (rnsd.status==Result.ERR_Backend) {
600             return Result.err(rnsd);
601         }
602
603         // Check if Access to Whole NS
604         // AAF-724 - Make consistent response for May User", and not take the
605         // last check... too confusing.
606         Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + pdd.ns + ":ns", access.name());
607         if (rv.isOK()) {
608             return rv;
609         } else {
610             return Result.err(Status.ERR_Denied,
611                     "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
612                     pdd.fullType(), pdd.instance, pdd.action);
613         }
614
615     }
616
617     public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
618         try {
619             Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
620             if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":"    + rnsd.value.name + ":ns", access.name()).isOK()) {
621                 return Result.ok();
622             }
623             boolean isUser = trans.user().equals(dd.user);
624             boolean isDelegate = dd.delegate != null
625                     && (dd.user.equals(dd.delegate) || trans.user().equals(
626                             dd.delegate));
627             Organization org = trans.org();
628             switch (access) {
629             case create:
630                 if (org.getIdentity(trans, dd.user) == null) {
631                     return Result.err(Status.ERR_UserNotFound,
632                             "[%s] is not a user in the company database.",
633                             dd.user);
634                 }
635                 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
636                     return Result.err(Status.ERR_UserNotFound,
637                             "[%s] is not a user in the company database.",
638                             dd.delegate);
639                 }
640                 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
641                     return Result.err(Status.ERR_BadData,
642                             "[%s] cannot be a delegate for self", dd.user);
643                 }
644                 if (!isUser    && !isGranted(trans, trans.user(), ROOT_NS,DELG,
645                                 org.getDomain(), Question.CREATE)) {
646                     return Result.err(Status.ERR_Denied,
647                             "[%s] may not create a delegate for [%s]",
648                             trans.user(), dd.user);
649                 }
650                 break;
651             case read:
652             case write:
653                 if (!isUser    && !isDelegate && 
654                         !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
655                     return Result.err(Status.ERR_Denied,
656                             "[%s] may not %s delegates for [%s]", trans.user(),
657                             access.name(), dd.user);
658                 }
659                 break;
660             default:
661                 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
662             }
663         } catch (Exception e) {
664             return Result.err(e);
665         }
666         return Result.ok();
667     }
668
669     /*
670      * Check (recursively, if necessary), if able to do something based on NS
671      */
672     private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user,    NsDAO.Data nsd, String ns_and_type, String access) {
673         String ns = nsd.name;
674
675         // If an ADMIN of the Namespace, then allow
676         
677         Result<List<UserRoleDAO.Data>> rurd;
678         if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
679             return Result.ok(nsd);
680         } else if (rurd.status==Result.ERR_Backend) {
681             return Result.err(rurd);
682         }
683         
684         // If Specially granted Global Permission
685         if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
686             return Result.ok(nsd);
687         }
688
689         // Check recur
690
691         int dot = ns.length();
692         if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
693             Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
694             if (rnsd.isOK()) {
695                 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
696             } else if (rnsd.status==Result.ERR_Backend) {
697                 return Result.err(rnsd);
698             }
699             if (rnsd.isOK()) {
700                 return Result.ok(nsd);
701             } else if (rnsd.status==Result.ERR_Backend) {
702                 return Result.err(rnsd);
703             }
704         }
705         return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
706                 ns_and_type);
707     }
708
709     
710     /**
711      * isGranted
712      * 
713      * Important function - Check internal Permission Schemes for Permission to
714      * do things
715      * 
716      * @param trans
717      * @param type
718      * @param instance
719      * @param action
720      * @return
721      */
722     public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
723         Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
724         if (perms.isOK()) {
725             for (PermDAO.Data pd : perms.value) {
726                 if (ns.equals(pd.ns)) {
727                     if (type.equals(pd.type)) {
728                         if (PermEval.evalInstance(pd.instance, instance)) {
729                             if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action 
730                                 return true;
731                             }
732                         }
733                     }
734                 }
735             }
736         }
737         return false;
738     }
739
740     public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
741         Result<List<CredDAO.Data>> result;
742         TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
743         try {
744             result = credDAO.readID(trans, user);
745         } finally {
746             tt.done();
747         }
748
749         Result<Date> rv = null;
750         if (result.isOK()) {
751             if (result.isEmpty()) {
752                 rv = Result.err(Status.ERR_UserNotFound, user);
753                 if (willSpecialLog(trans,user)) {
754                     trans.audit().log("Special DEBUG:", user, " does not exist in DB");
755                 }
756             } else {
757                 Date now = new Date();//long now = System.currentTimeMillis();
758                 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.     
759                 List<CredDAO.Data> cddl;
760                 if (result.value.size() > 1) {
761                     cddl = new ArrayList<>(result.value.size());
762                     for (CredDAO.Data old : result.value) {
763                         if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
764                             cddl.add(old);
765                         }
766                     }
767                     if (cddl.size()>1) {
768                         Collections.sort(cddl,new Comparator<CredDAO.Data>() {
769                             @Override
770                             public int compare(org.onap.aaf.auth.dao.cass.CredDAO.Data a,
771                                                org.onap.aaf.auth.dao.cass.CredDAO.Data b) {
772                                 return b.expires.compareTo(a.expires);
773                             }
774                         });
775                     }
776                 } else {
777                     cddl = result.value;
778                 }
779     
780                 Date expired = null;
781                 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
782                 for (CredDAO.Data cdd : cddl) {
783                     if (!cdd.id.equals(user)) {
784                         trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
785                     }
786                     if (cdd.expires.after(now)) {
787                         byte[] dbcred = cdd.cred.array();
788                         
789                         try {
790                             switch(cdd.type) {
791                                 case CredDAO.BASIC_AUTH:
792                                     byte[] md5=Hash.hashMD5(cred);
793                                     if (Hash.compareTo(md5,dbcred)==0) {
794                                         checkLessThanDays(trans,7,now,cdd);
795                                         return Result.ok(cdd.expires);
796                                     } else if (debug!=null) {
797                                         load(debug, cdd);
798                                     }
799                                     break;
800                                 case CredDAO.BASIC_AUTH_SHA256:
801                                     ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
802                                     bb.putInt(cdd.other);
803                                     bb.put(cred);
804                                     byte[] hash = Hash.hashSHA256(bb.array());
805     
806                                     if (Hash.compareTo(hash,dbcred)==0) {
807                                         checkLessThanDays(trans,7,now,cdd);
808                                         return Result.ok(cdd.expires);
809                                     } else if (debug!=null) {
810                                         load(debug, cdd);
811                                     }
812                                     break;
813                                 default:
814                                     trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
815                             }
816                         } catch (NoSuchAlgorithmException e) {
817                             trans.error().log(e);
818                         }
819                     } else {
820                         if (expired==null || expired.before(cdd.expires)) {
821                             expired = cdd.expires;
822                         }
823                     }
824                 } // end for each
825                 if (debug==null) {
826                     trans.audit().printf("No cred matches ip=%s, user=%s\n",trans.ip(),user);
827                 } else {
828                     trans.audit().printf("No cred matches ip=%s, user=%s %s\n",trans.ip(),user,debug.toString());
829                 }
830                 if (expired!=null) {
831                     // Note: this is only returned if there are no good Credentials
832                     rv = Result.err(Status.ERR_Security,
833                             "Credentials %s from %s expired %s",trans.user(), trans.ip(), Chrono.dateTime(expired));
834                 }
835             }
836         } else {
837             return Result.err(result);
838         }
839         return rv == null ? Result.create((Date) null, Status.ERR_Security, "Wrong credential") : rv;
840     }
841
842
843     private void load(StringBuilder debug, Data cdd) {
844         debug.append("DB Entry: user=");
845         debug.append(cdd.id);
846         debug.append(",type=");
847         debug.append(cdd.type);
848         debug.append(",expires=");
849         debug.append(Chrono.dateTime(cdd.expires));
850         debug.append('\n');
851     }
852
853
854     private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
855         long close = now.getTime() + (days * 86400000);
856         long cexp=cdd.expires.getTime();
857         if (cexp<close) {
858             int daysLeft = days-(int)((close-cexp)/86400000);
859             trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,msg=\"Password expires in less than %d day%s\"",
860                 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, daysLeft,daysLeft==1?"":"s");
861         }
862     }
863
864
865     public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
866         if (cred.type==CredDAO.RAW) {
867             TimeTaken tt = trans.start("Hash Cred", Env.SUB);
868             try {
869                 cred.type = CredDAO.BASIC_AUTH_SHA256;
870                 cred.other = random.nextInt();
871                 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
872                 bb.putInt(cred.other);
873                 bb.put(cred.cred);
874                 byte[] hash = Hash.hashSHA256(bb.array());
875                 cred.cred = ByteBuffer.wrap(hash);
876                 return Result.ok(cred);
877             } catch (NoSuchAlgorithmException e) {
878                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
879             } finally {
880                 tt.done();
881             }
882             
883         }
884         return Result.err(Status.ERR_Security,"invalid/unreadable credential");
885     }
886     
887     public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
888             TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
889             try {
890                 switch(orig.type) {
891                     case CredDAO.BASIC_AUTH_SHA256:
892                         ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
893                         bb.putInt(orig.other);
894                         bb.put(raw);
895                         return Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
896                     case CredDAO.BASIC_AUTH:
897                         return Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
898                     default:
899                         return Result.ok(false);
900                 }
901             } catch (NoSuchAlgorithmException e) {
902                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
903             } finally {
904                 tt.done();
905             }
906     }
907
908     public static final String APPROVED = "APPROVE";
909     public static final String REJECT = "REJECT";
910     public static final String PENDING = "PENDING";
911
912     public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
913             List<ApprovalDAO.Data> approvals) {
914         // get the approval policy for the organization
915
916         // get the list of approvals with an accept status
917
918         // validate the approvals against the policy
919
920         // for now check if all approvals are received and return
921         // SUCCESS/FAILURE/SKIP
922         boolean bReject = false;
923         boolean bPending = false;
924
925         for (ApprovalDAO.Data approval : approvals) {
926             if (approval.status.equals(REJECT)) {
927                 bReject = true;
928             } else if (approval.status.equals(PENDING)) {
929                 bPending = true;
930             }
931         }
932         if (bReject) {
933             return Result.err(Status.ERR_Policy,
934                     "Approval Polocy not conformed");
935         }
936         if (bPending) {
937             return Result.err(Status.ERR_ActionNotCompleted,
938                     "Required Approvals not received");
939         }
940
941         return Result.ok();
942     }
943
944     private static final String NO_CACHE_NAME = "No Cache Data named %s";
945
946     public Result<Void> clearCache(AuthzTrans trans, String cname) {
947         boolean all = "all".equals(cname);
948         Result<Void> rv = null;
949
950         if (all || NsDAO.TABLE.equals(cname)) {
951             int seg[] = series(NsDAO.CACHE_SEG);
952             for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
953             rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
954         }
955         if (all || PermDAO.TABLE.equals(cname)) {
956             int seg[] = series(PermDAO.CACHE_SEG);
957             for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
958             rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
959         }
960         if (all || RoleDAO.TABLE.equals(cname)) {
961             int seg[] = series(RoleDAO.CACHE_SEG);
962             for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
963             rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
964         }
965         if (all || UserRoleDAO.TABLE.equals(cname)) {
966             int seg[] = series(UserRoleDAO.CACHE_SEG);
967             for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
968             rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
969         }
970         if (all || CredDAO.TABLE.equals(cname)) {
971             int seg[] = series(CredDAO.CACHE_SEG);
972             for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
973             rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
974         }
975         if (all || CertDAO.TABLE.equals(cname)) {
976             int seg[] = series(CertDAO.CACHE_SEG);
977             for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
978             rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
979         }
980
981         if (rv == null) {
982             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
983         }
984         return rv;
985     }
986
987     public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
988         Result<Void> rv;
989         if (NsDAO.TABLE.equals(cname)) {
990             rv = nsDAO.invalidate(segment);
991         } else if (PermDAO.TABLE.equals(cname)) {
992             rv = permDAO.invalidate(segment);
993         } else if (RoleDAO.TABLE.equals(cname)) {
994             rv = roleDAO.invalidate(segment);
995         } else if (UserRoleDAO.TABLE.equals(cname)) {
996             rv = userRoleDAO.invalidate(segment);
997         } else if (CredDAO.TABLE.equals(cname)) {
998             rv = credDAO.invalidate(segment);
999         } else if (CertDAO.TABLE.equals(cname)) {
1000             rv = certDAO.invalidate(segment);
1001         } else {
1002             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1003         }
1004         return rv;
1005     }
1006
1007     private int[] series(int max) {
1008         int[] series = new int[max];
1009         for (int i = 0; i < max; ++i)
1010             series[i] = i;
1011         return series;
1012     }
1013
1014     public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1015         Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1016         if (userDelegatedFor==null) {
1017             userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1018             rldd.put(user, userDelegatedFor);
1019         }
1020         if (userDelegatedFor.isOKhasData()) {
1021             for (DelegateDAO.Data curr : userDelegatedFor.value) {
1022                 if (curr.user.equals(approver) && curr.delegate.equals(user)
1023                         && curr.expires.after(new Date())) {
1024                     return true;
1025                 }
1026             }
1027         }
1028         return false;
1029     }
1030
1031     public static boolean willSpecialLog(AuthzTrans trans, String user) {
1032         Boolean b = trans.get(specialLogSlot, null);
1033         if (b==null) { // we haven't evaluated in this trans for Special Log yet
1034             if (specialLog==null) {
1035                 return false;
1036             } else {
1037                 b = specialLog.contains(user);
1038                 trans.put(specialLogSlot, b);
1039             }
1040         }
1041         return b;
1042     }
1043     
1044     public static void logEncryptTrace(AuthzTrans trans, String data) {
1045         long ti;
1046         trans.put(transIDSlot, ti=nextTraceID());
1047         trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1048     }
1049
1050     private synchronized static long nextTraceID() {
1051         return ++traceID;
1052     }
1053
1054     public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1055         if (specialLog == null) {
1056             specialLog = new HashSet<>();
1057         }
1058         boolean rc = specialLog.add(id);
1059         if (rc) {
1060             trans.trace().printf("Trace on for %s requested by %s",id,trans.user());            
1061         }
1062         return rc;
1063     }
1064
1065     public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1066         if (specialLog==null) {
1067             return false;
1068         }
1069         boolean rv = specialLog.remove(id);
1070         if (specialLog.isEmpty()) {
1071             specialLog = null;
1072         }
1073         if (rv) {
1074             trans.trace().printf("Trace off for %s requested by %s",id,trans.user());            
1075         }
1076         return rv;
1077     }
1078
1079     /** 
1080      * canMove
1081      * Which Types can be moved
1082      * @param nsType
1083      * @return
1084      */
1085     public boolean canMove(NsType nsType) {
1086         boolean rv;
1087         switch(nsType) {
1088             case DOT:
1089             case ROOT:
1090             case COMPANY:
1091             case UNKNOWN:
1092                 rv = false;
1093                 break;
1094             default:
1095                 rv = true;
1096         }
1097         return rv;
1098     }
1099
1100     public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1101         Date now = new Date();
1102         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1103         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1104             if (urdd.expires.after(now)) {
1105                 return true;
1106             }
1107         }};
1108         return false;
1109     }
1110     
1111     public boolean isOwner(AuthzTrans trans, String user, String ns) {
1112         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_OWNER);
1113         Date now = new Date();
1114         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1115             if (urdd.expires.after(now)) {
1116                 return true;
1117             }
1118         }};
1119         return false;
1120     }
1121
1122     public int countOwner(AuthzTrans trans, String ns) {
1123         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.readByRole(trans,ns+DOT_OWNER);
1124         Date now = new Date();
1125         int count = 0;
1126         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1127             if (urdd.expires.after(now)) {
1128                 ++count;
1129             }
1130         }};
1131         return count;
1132     }
1133     
1134     /**
1135      * Return a Unique String, (same string, if it is already unique), with only
1136      * lowercase letters, digits and the '.' character.
1137      * 
1138      * @param name
1139      * @return
1140      * @throws IOException 
1141      */
1142     public static String toUnique(String name) throws IOException {
1143         byte[] from = name.getBytes();
1144         StringBuilder sb = new StringBuilder();
1145         byte f;
1146         for (int i=0;i<from.length;++i) {
1147             f=(byte)(from[i]); // printables;
1148             sb.append((char)((f>>4)+0x61));
1149             sb.append((char)((f&0x0F)+0x61));
1150         }
1151         return sb.toString();
1152     }
1153     
1154     public static String fromUnique(String name) throws IOException {
1155         byte[] from = name.getBytes();
1156         StringBuilder sb = new StringBuilder();
1157         char c;
1158         for (int i=0;i<from.length;++i) {
1159             c = (char)((from[i]-0x61)<<4);
1160             c |= (from[++i]-0x61);
1161             sb.append(c);
1162         }
1163         return sb.toString();
1164     }
1165
1166 }