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