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