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