changed to unmaintained
[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) {
790                         String supportedDomain = org.supportedDomain(dd.user);
791                         if(supportedDomain==null) {
792                         return Result.err(Status.ERR_Denied,
793                                 "[%s] may not create a delegate for the domain for [%s]",
794                                 trans.user(), dd.user);
795                         } else if(!isGranted(trans, trans.user(), ROOT_NS,DELG,supportedDomain,Question.CREATE)) {
796                                 return Result.err(Status.ERR_Denied,
797                             "[%s] may not create a delegate for [%s]",
798                             trans.user(), dd.user);
799                         }
800                 }
801                 break;
802             case read:
803             case write:
804                 if (!isUser    && !isDelegate &&
805                         !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
806                     return Result.err(Status.ERR_Denied,
807                             "[%s] may not %s delegates for [%s]", trans.user(),
808                             access.name(), dd.user);
809                 }
810                 break;
811             default:
812                 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
813             }
814         } catch (Exception e) {
815             return Result.err(e);
816         }
817         return Result.ok();
818     }
819
820     /*
821      * Check (recursively, if necessary), if able to do something based on NS
822      */
823     private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user,    NsDAO.Data nsd, String ns_and_type, String access) {
824         String ns = nsd.name;
825
826         // If an ADMIN of the Namespace, then allow
827
828         Result<List<UserRoleDAO.Data>> rurd;
829         if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
830             return Result.ok(nsd);
831         } else if (rurd.status==Result.ERR_Backend) {
832             return Result.err(rurd);
833         }
834
835         // If Specially granted Global Permission
836         if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
837             return Result.ok(nsd);
838         }
839
840         // Check recur
841
842         int dot = ns.length();
843         if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
844             Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
845             if (rnsd.isOK()) {
846                 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
847             } else if (rnsd.status==Result.ERR_Backend) {
848                 return Result.err(rnsd);
849             }
850             if (rnsd.isOK()) {
851                 return Result.ok(nsd);
852             } else if (rnsd.status==Result.ERR_Backend) {
853                 return Result.err(rnsd);
854             }
855         }
856         return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
857                 ns_and_type);
858     }
859
860
861     /**
862      * isGranted
863      *
864      * Important function - Check internal Permission Schemes for Permission to
865      * do things
866      *
867      * @param trans
868      * @param type
869      * @param instance
870      * @param action
871      * @return
872      */
873     public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
874         Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
875         if (perms.isOK()) {
876             for (PermDAO.Data pd : perms.value) {
877                 if (ns.equals(pd.ns)) {
878                     if (type.equals(pd.type)) {
879                         if (PermEval.evalInstance(pd.instance, instance)) {
880                             if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
881                                 return true;
882                             }
883                         }
884                     }
885                 }
886             }
887         }
888         return false;
889     }
890
891     public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
892         Result<List<CredDAO.Data>> result;
893         TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
894         try {
895             result = credDAO.readIDBAth(trans, user);
896         } finally {
897             tt.done();
898         }
899
900         Result<Date> rv = null;
901         if (result.isOK()) {
902             if (result.isEmpty()) {
903                 rv = Result.err(Status.ERR_UserNotFound, user);
904                 if (willSpecialLog(trans,user)) {
905                     trans.audit().log("Special DEBUG:", user, " does not exist in DB");
906                 }
907             } else {
908                 Date now = new Date();
909                 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
910                 // 9/14/2019. Use TreeSet for sorting, and using only the LAST of a Tagged entry
911                 Collection<CredDAO.Data> cddl;
912                 if (result.value.size() > 1) {
913                     Map<String,CredDAO.Data> mcdd = new TreeMap<>();
914                     CredDAO.Data cdd;
915                     String tag;
916                     int pseudoTag = 0;
917                     for (CredDAO.Data rcdd : result.value) {
918                         if (rcdd.type==CredDAO.BASIC_AUTH || rcdd.type==CredDAO.BASIC_AUTH_SHA256) {
919                             if(rcdd.tag==null) {
920                                 mcdd.put(Integer.toString(++pseudoTag),rcdd);
921                             } else {
922                                 tag = rcdd.tag;
923                                 cdd = mcdd.get(tag);
924                                 if(cdd==null || cdd.expires.before(rcdd.expires)) {
925                                     mcdd.put(tag,rcdd);
926                                 }
927                             }
928                         }
929                     }
930                     cddl = mcdd.values();
931                 } else {
932                     cddl = result.value;
933                 }
934
935                 Date expired = null;
936                 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
937                 for (CredDAO.Data cdd : cddl) {
938                     if (!cdd.id.equals(user)) {
939                         trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
940                     }
941                     if (cdd.expires.after(now) || trans.org().isUserExpireExempt(cdd.id, cdd.expires)) {
942                         byte[] dbcred = cdd.cred.array();
943
944                         try {
945                             switch(cdd.type) {
946                                 case CredDAO.BASIC_AUTH:
947                                     byte[] md5=Hash.hashMD5(cred);
948                                     if (Hash.compareTo(md5,dbcred)==0) {
949                                         checkLessThanDays(trans,cldays,now,cdd);
950                                         trans.setTag(cdd.tag);
951                                         return Result.ok(cdd.expires);
952                                     } else if (debug!=null) {
953                                         load(debug, cdd);
954                                     }
955                                     break;
956                                 case CredDAO.BASIC_AUTH_SHA256:
957                                     ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
958                                     bb.putInt(cdd.other);
959                                     bb.put(cred);
960                                     byte[] hash = Hash.hashSHA256(bb.array());
961
962                                     if (Hash.compareTo(hash,dbcred)==0) {
963                                         checkLessThanDays(trans,cldays,now,cdd);
964                                         trans.setTag(cdd.tag);
965                                         return Result.ok(cdd.expires);
966                                     } else if (debug!=null) {
967                                         load(debug, cdd);
968                                     }
969                                     break;
970                                 default:
971                                     trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
972                             }
973                         } catch (NoSuchAlgorithmException e) {
974                             trans.error().log(e);
975                         }
976                     } else {
977                         if (expired==null || expired.before(cdd.expires)) {
978                             expired = cdd.expires;
979                             trans.setTag(cdd.tag);
980                         }
981                     }
982                 } // end for each
983
984                 if (expired!=null) {
985                     // Note: this is only returned if there are no good Credentials
986                     rv = Result.err(Status.ERR_Security,
987                             "Credentials expired %s",Chrono.utcStamp(expired));
988                 } else {
989                     if (debug==null && alwaysSpecial) {
990                         debug = new StringBuilder();
991                     }
992                     if (debug!=null) {
993                         debug.append(trans.env().encryptor().encrypt(new String(cred)));
994                         rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
995                     }
996                 }
997             }
998         } else {
999             return Result.err(result);
1000         }
1001         return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
1002     }
1003
1004
1005     private void load(StringBuilder debug, Data cdd) {
1006         debug.append("\nDB Entry: user=");
1007         debug.append(cdd.id);
1008         debug.append(",type=");
1009         debug.append(cdd.type);
1010         debug.append(",expires=");
1011         debug.append(Chrono.dateTime(cdd.expires));
1012         debug.append(",tag=");
1013         debug.append(cdd.tag);
1014         debug.append('\n');
1015     }
1016
1017
1018     private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1019         long close = now.getTime() + (days * 86400000);
1020         long cexp=cdd.expires.getTime();
1021         if (cexp<close) {
1022             int daysLeft = days-(int)((close-cexp)/86400000);
1023             trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1024                 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
1025                 daysLeft,daysLeft==1?"":"s");
1026         }
1027     }
1028
1029
1030     public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1031         if (cred.type==CredDAO.RAW) {
1032             TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1033             try {
1034                 cred.type = CredDAO.BASIC_AUTH_SHA256;
1035                 cred.other = random.nextInt();
1036                 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1037                 bb.putInt(cred.other);
1038                 bb.put(cred.cred);
1039                 byte[] hash = Hash.hashSHA256(bb.array());
1040                 cred.cred = ByteBuffer.wrap(hash);
1041                 return Result.ok(cred);
1042             } catch (NoSuchAlgorithmException e) {
1043                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1044             } finally {
1045                 tt.done();
1046             }
1047
1048         } else if (cred.type==CredDAO.FQI) {
1049             cred.cred = null;
1050             return Result.ok(cred);
1051         }
1052         return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1053     }
1054
1055     public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1056         Result<Boolean> rv;
1057         TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1058         try {
1059             switch(orig.type) {
1060                 case CredDAO.BASIC_AUTH_SHA256:
1061                     ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1062                     bb.putInt(orig.other);
1063                     bb.put(raw);
1064                     rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1065                 case CredDAO.BASIC_AUTH:
1066                     rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1067                 case CredDAO.FQI:
1068                 default:
1069                     rv = Result.ok(false);
1070             }
1071         } catch (NoSuchAlgorithmException e) {
1072             rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1073         } finally {
1074             tt.done();
1075         }
1076         return rv;
1077     }
1078
1079     public static final String APPROVED = "APPROVE";
1080     public static final String REJECT = "REJECT";
1081     public static final String PENDING = "PENDING";
1082
1083     public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1084             List<ApprovalDAO.Data> approvals) {
1085         // get the approval policy for the organization
1086
1087         // get the list of approvals with an accept status
1088
1089         // validate the approvals against the policy
1090
1091         // for now check if all approvals are received and return
1092         // SUCCESS/FAILURE/SKIP
1093         boolean bReject = false;
1094         boolean bPending = false;
1095
1096         for (ApprovalDAO.Data approval : approvals) {
1097             if (approval.status.equals(REJECT)) {
1098                 bReject = true;
1099             } else if (approval.status.equals(PENDING)) {
1100                 bPending = true;
1101             }
1102         }
1103         if (bReject) {
1104             return Result.err(Status.ERR_Policy,
1105                     "Approval Polocy not conformed");
1106         }
1107         if (bPending) {
1108             return Result.err(Status.ERR_ActionNotCompleted,
1109                     "Required Approvals not received");
1110         }
1111
1112         return Result.ok();
1113     }
1114
1115     private static final String NO_CACHE_NAME = "No Cache Data named %s";
1116
1117     public Result<Void> clearCache(AuthzTrans trans, String cname) {
1118         boolean all = "all".equals(cname);
1119         Result<Void> rv = null;
1120
1121         if (all || NsDAO.TABLE.equals(cname)) {
1122             int[] seg = series(NsDAO.CACHE_SEG);
1123             for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1124             rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1125         }
1126         if (all || PermDAO.TABLE.equals(cname)) {
1127             int[] seg = series(PermDAO.CACHE_SEG);
1128             for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1129             rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1130         }
1131         if (all || RoleDAO.TABLE.equals(cname)) {
1132             int[] seg = series(RoleDAO.CACHE_SEG);
1133             for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1134             rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1135         }
1136         if (all || UserRoleDAO.TABLE.equals(cname)) {
1137             int[] seg = series(UserRoleDAO.CACHE_SEG);
1138             for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1139             rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1140         }
1141         if (all || CredDAO.TABLE.equals(cname)) {
1142             int[] seg = series(CredDAO.CACHE_SEG);
1143             for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1144             rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1145         }
1146         if (all || CertDAO.TABLE.equals(cname)) {
1147             int[] seg = series(CertDAO.CACHE_SEG);
1148             for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1149             rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1150         }
1151
1152         if (rv == null) {
1153             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1154         }
1155         return rv;
1156     }
1157
1158     public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1159         Result<Void> rv;
1160         if (NsDAO.TABLE.equals(cname)) {
1161             rv = nsDAO.invalidate(segment);
1162         } else if (PermDAO.TABLE.equals(cname)) {
1163             rv = permDAO.invalidate(segment);
1164         } else if (RoleDAO.TABLE.equals(cname)) {
1165             rv = roleDAO.invalidate(segment);
1166         } else if (UserRoleDAO.TABLE.equals(cname)) {
1167             rv = userRoleDAO.invalidate(segment);
1168         } else if (CredDAO.TABLE.equals(cname)) {
1169             rv = credDAO.invalidate(segment);
1170         } else if (CertDAO.TABLE.equals(cname)) {
1171             rv = certDAO.invalidate(segment);
1172         } else {
1173             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1174         }
1175         return rv;
1176     }
1177
1178     private int[] series(int max) {
1179         int[] series = new int[max];
1180         for (int i = 0; i < max; ++i)
1181             series[i] = i;
1182         return series;
1183     }
1184
1185     public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1186         Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1187         if (userDelegatedFor==null) {
1188             userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1189             rldd.put(user, userDelegatedFor);
1190         }
1191         if (userDelegatedFor.isOKhasData()) {
1192             for (DelegateDAO.Data curr : userDelegatedFor.value) {
1193                 if (curr.user.equals(approver) && curr.delegate.equals(user)
1194                         && curr.expires.after(new Date())) {
1195                     return true;
1196                 }
1197             }
1198         }
1199         return false;
1200     }
1201
1202     public static boolean willSpecialLog(AuthzTrans trans, String user) {
1203         Boolean b = trans.get(specialLogSlot, null);
1204         if (b==null) { // we haven't evaluated in this trans for Special Log yet
1205             if (specialLog==null) {
1206                 return false;
1207             } else {
1208                 b = specialLog.contains(user);
1209                 trans.put(specialLogSlot, b);
1210             }
1211         }
1212         return b;
1213     }
1214
1215     public static void logEncryptTrace(AuthzTrans trans, String data) {
1216         long ti;
1217         trans.put(transIDSlot, ti=nextTraceID());
1218         trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1219     }
1220
1221     private synchronized static long nextTraceID() {
1222         return ++traceID;
1223     }
1224
1225     public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1226         if (specialLog == null) {
1227             specialLog = new HashSet<>();
1228         }
1229         boolean rc = specialLog.add(id);
1230         if (rc) {
1231             trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1232         }
1233         return rc;
1234     }
1235
1236     public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1237         if (specialLog==null) {
1238             return false;
1239         }
1240         boolean rv = specialLog.remove(id);
1241         if (specialLog.isEmpty()) {
1242             specialLog = null;
1243         }
1244         if (rv) {
1245             trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1246         }
1247         return rv;
1248     }
1249
1250     /**
1251      * canMove
1252      * Which Types can be moved
1253      * @param nsType
1254      * @return
1255      */
1256     public boolean canMove(NsType nsType) {
1257         boolean rv;
1258         switch(nsType) {
1259             case DOT:
1260             case ROOT:
1261             case COMPANY:
1262             case UNKNOWN:
1263                 rv = false;
1264                 break;
1265             default:
1266                 rv = true;
1267         }
1268         return rv;
1269     }
1270
1271     public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1272         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1273         if (rur.isOKhasData()) {
1274             Date now = new Date();
1275             for (UserRoleDAO.Data urdd : rur.value){
1276                 if (urdd.expires.after(now) || trans.org().isUserExpireExempt(urdd.user, urdd.expires)) {
1277                     return true;
1278                 }
1279             }
1280         };
1281         return false;
1282     }
1283
1284     public boolean isOwner(AuthzTrans trans, String user, String ns) {
1285         Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1286         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1287             Date now = new Date();
1288             if (urdd.expires.after(now) || trans.org().isUserExpireExempt(urdd.user, urdd.expires)) {
1289                 return true;
1290             }
1291         }};
1292         return false;
1293     }
1294
1295     public int countOwner(AuthzTrans trans, String ns) {
1296         Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1297         Date now = new Date();
1298         int count = 0;
1299         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1300             if (urdd.expires.after(now) || trans.org().isUserExpireExempt(urdd.user, urdd.expires)) {
1301                 ++count;
1302             }
1303         }};
1304         return count;
1305     }
1306
1307     /**
1308      * Return a Unique String, (same string, if it is already unique), with only
1309      * lowercase letters, digits and the '.' character.
1310      *
1311      * @param name
1312      * @return
1313      * @throws IOException
1314      */
1315     public static String toUnique(String name) throws IOException {
1316         byte[] from = name.getBytes();
1317         StringBuilder sb = new StringBuilder();
1318         byte f;
1319         for (int i=0;i<from.length;++i) {
1320             f=(byte)(from[i]); // printables;
1321             sb.append((char)((f>>4)+0x61));
1322             sb.append((char)((f&0x0F)+0x61));
1323         }
1324         return sb.toString();
1325     }
1326
1327     public static String fromUnique(String name) throws IOException {
1328         byte[] from = name.getBytes();
1329         StringBuilder sb = new StringBuilder();
1330         char c;
1331         for (int i=0;i<from.length;++i) {
1332             c = (char)((from[i]-0x61)<<4);
1333             c |= (from[++i]-0x61);
1334             sb.append(c);
1335         }
1336         return sb.toString();
1337     }
1338
1339 }