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