1b20b900b5c5490940888208d628fbf5f3caa76e
[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                 if (type.type == nsd.type) {
500                     return Result.ok(nsd);
501                 } else {
502                     int dot = str.lastIndexOf('.');
503                     
504                     if (dot < 0) {
505                         return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
506                     } else {
507                         return deriveFirstNsForType(trans, str.substring(0, dot),type);
508                     }
509                 }
510             } else {
511                 int dot = str.lastIndexOf('.');
512                 
513                 if (dot < 0) {
514                     return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
515                 } else {
516                     return deriveFirstNsForType(trans, str.substring(0, dot),type);
517                 }
518             }
519         }
520         return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
521     }
522
523     public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
524         Result<NsDAO.Data> ndd = deriveNs(trans, child);
525         if (ndd.isOK()) {
526             NsSplit nss = new NsSplit(ndd.value, child);
527             if (nss.isOK()) {
528                 return Result.ok(nss);
529             } else {
530                 return Result.err(Status.ERR_NsNotFound,
531                         "Cannot split [%s] into valid namespace elements",
532                         child);
533             }
534         }
535         return Result.err(ndd);
536     }
537
538     /**
539      * Translate an ID into it's domain
540      * 
541      * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
542      * 
543      * @param id
544      * @return
545      */
546     public static String domain2ns(String id) {
547         int at = id.indexOf('@');
548         if (at >= 0) {
549             String[] domain = id.substring(at + 1).split("\\.");
550             StringBuilder ns = new StringBuilder(id.length());
551             boolean first = true;
552             for (int i = domain.length - 1; i >= 0; --i) {
553                 if (first) {
554                     first = false;
555                 } else {
556                     ns.append('.');
557                 }
558                 ns.append(domain[i]);
559             }
560             return ns.toString();
561         } else {
562             return "";
563         }
564
565     }
566
567     /**
568      * Validate Namespace of ID@Domain
569      * 
570      * Namespace is reverse order of Domain.
571      * 
572      * @param trans
573      * @param id
574      * @return
575      */
576     public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
577         // Take domain, reverse order, and check on NS
578         String ns;
579         if (id.indexOf('@')<0) { // it's already an ns, not an ID
580             ns = id;
581         } else {
582             ns = domain2ns(id);
583         }
584         if (ns.length() > 0) {
585             if (!trans.org().getDomain().equals(ns)) { 
586                 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
587                 if (rlnsd.isOKhasData()) {
588                     return Result.ok(rlnsd.value.get(0));
589                 }
590             }
591         }
592         return Result.err(Status.ERR_NsNotFound,
593                 "A Namespace is not available for %s", id);
594     }
595
596     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
597         // <ns>.access|:role:<role name>|<read|write>
598         String ns = ndd.name;
599         boolean isRoot = ns.startsWith(Define.ROOT_NS());
600         int last;
601         do {
602             if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
603                 return Result.ok(ndd);
604             }
605             if(isRoot) {
606                 break;
607             }
608             if ((last = ns.lastIndexOf('.')) >= 0) {
609                 ns = ns.substring(0, last);
610             }
611         } while (last >= 0);
612         
613         // SAFETY - Do not allow these when NS is Root
614         if(!isRoot) {
615             // com.att.aaf.ns|:<client ns>:ns|<access>
616             // AAF-724 - Make consistent response for May User", and not take the
617             // last check... too confusing.
618             Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + ndd.name + ":ns", access.name());
619                 if (rv.isOK()) {
620                     return rv;
621                 } else if (rv.status==Result.ERR_Backend) {
622                     return Result.err(rv);
623                 }
624             }
625         return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
626                 user, access.name(), ndd.name);
627
628     }
629
630     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
631         if(trans.user().equals(rdd.ns)) {
632             return Result.ok((NsDAO.Data)null);
633         }
634         Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
635         if (rnsd.isOK()) {
636             return mayUser(trans, user, rnsd.value, rdd, access);
637         }
638         return rnsd;
639     }
640
641     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
642         // 1) For "read", Is User in the Role is enough
643         if(Access.read.equals(access)) {
644             Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
645             if (rurd.isOKhasData()) {
646                 return Result.ok(ndd);
647             }
648         }
649
650         String roleInst = ":role:" + rdd.name;
651         // <ns>.access|:role:<role name>|<read|write>
652         String ns = rdd.ns;
653         boolean isRoot = ns.startsWith(Define.ROOT_NS());
654         int last;
655         do {
656             if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
657                 return Result.ok(ndd);
658             }
659             if(isRoot) {
660                 break;
661             }
662             if ((last = ns.lastIndexOf('.')) >= 0) {
663                 ns = ns.substring(0, last);
664             }
665         } while (last >= 0);
666
667         // SAFETY - Do not allow these when NS is Root
668         if(!isRoot) {
669             // Check if Access by Global Role perm
670             // com.att.aaf.ns|:<client ns>:role:name|<access>
671             Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
672                     + rdd.ns + roleInst, access.name());
673             if (rnsd.isOK()) {
674                 return rnsd;
675             } else if (rnsd.status==Result.ERR_Backend) {
676                 return Result.err(rnsd);
677             }
678
679             // Check if Access to Whole NS
680             // AAF-724 - Make consistent response for May User", and not take the
681             // last check... too confusing.
682             Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, 
683                     ":" + rdd.ns + ":ns", access.name());
684             if (rv.isOK()) {
685                 return rv;
686             } else if (rnsd.status==Result.ERR_Backend) {
687                 return Result.err(rnsd);
688             }
689         }
690         return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
691                     user, access.name(), rdd.fullName());
692     }
693
694     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
695         if(pdd.ns.indexOf('@')>-1) {
696             if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
697                 NsDAO.Data ndd = new NsDAO.Data();
698                 ndd.name = user;
699                 ndd.type = NsDAO.USER;
700                 ndd.parent = "";
701                 return Result.ok(ndd);
702             } else {
703                 return Result.err(Result.ERR_Security,"Only a User may modify User");
704             }
705         }
706         Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
707         if (rnsd.isOK()) {
708             return mayUser(trans, user, rnsd.value, pdd, access);
709         }
710         return rnsd;
711     }
712
713     public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
714         // Most common occurrence... if granted Permission
715         if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
716             return Result.ok(ndd);
717         }
718         
719         String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
720         // <ns>.access|:role:<role name>|<read|write>
721         String ns = ndd.name;
722         boolean isRoot = ns.startsWith(Define.ROOT_NS());
723         int last;
724         do {
725             if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
726                 return Result.ok(ndd);
727             }
728             if(isRoot) {
729                 break;
730             }
731             if ((last = ns.lastIndexOf('.')) >= 0) {
732                 ns = ns.substring(0, last);
733             }
734         } while (last >= 0);
735
736         // SAFETY - Do not allow these when NS is Root
737         if(!isRoot) {
738             // Check if Access by NS perm
739             // com.att.aaf.ns|:<client ns>:role:name|<access>
740             Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
741             if (rnsd.isOK()) {
742                 return rnsd;
743             } else if (rnsd.status==Result.ERR_Backend) {
744                 return Result.err(rnsd);
745             }
746
747             // Check if Access to Whole NS
748             // AAF-724 - Make consistent response for May User", and not take the
749             // last check... too confusing.
750             Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":"    + pdd.ns + ":ns", access.name());
751             if (rv.isOK()) {
752                 return rv;
753             }
754         }
755         return Result.err(Status.ERR_Denied,
756                 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
757                 pdd.fullType(), pdd.instance, pdd.action);
758     }
759
760     public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
761         try {
762             Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
763             if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":"    + rnsd.value.name + ":ns", access.name()).isOK()) {
764                 return Result.ok();
765             }
766             boolean isUser = trans.user().equals(dd.user);
767             boolean isDelegate = dd.delegate != null
768                     && (dd.user.equals(dd.delegate) || trans.user().equals(
769                             dd.delegate));
770             Organization org = trans.org();
771             switch (access) {
772             case create:
773                 if (org.getIdentity(trans, dd.user) == null) {
774                     return Result.err(Status.ERR_UserNotFound,
775                             "[%s] is not a user in the company database.",
776                             dd.user);
777                 }
778                 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
779                     return Result.err(Status.ERR_UserNotFound,
780                             "[%s] is not a user in the company database.",
781                             dd.delegate);
782                 }
783                 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
784                     return Result.err(Status.ERR_BadData,
785                             "[%s] cannot be a delegate for self", dd.user);
786                 }
787                 if (!isUser    && !isGranted(trans, trans.user(), ROOT_NS,DELG,
788                                 org.getDomain(), Question.CREATE)) {
789                     return Result.err(Status.ERR_Denied,
790                             "[%s] may not create a delegate for [%s]",
791                             trans.user(), dd.user);
792                 }
793                 break;
794             case read:
795             case write:
796                 if (!isUser    && !isDelegate && 
797                         !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
798                     return Result.err(Status.ERR_Denied,
799                             "[%s] may not %s delegates for [%s]", trans.user(),
800                             access.name(), dd.user);
801                 }
802                 break;
803             default:
804                 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
805             }
806         } catch (Exception e) {
807             return Result.err(e);
808         }
809         return Result.ok();
810     }
811
812     /*
813      * Check (recursively, if necessary), if able to do something based on NS
814      */
815     private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user,    NsDAO.Data nsd, String ns_and_type, String access) {
816         String ns = nsd.name;
817
818         // If an ADMIN of the Namespace, then allow
819         
820         Result<List<UserRoleDAO.Data>> rurd;
821         if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
822             return Result.ok(nsd);
823         } else if (rurd.status==Result.ERR_Backend) {
824             return Result.err(rurd);
825         }
826         
827         // If Specially granted Global Permission
828         if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
829             return Result.ok(nsd);
830         }
831
832         // Check recur
833
834         int dot = ns.length();
835         if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
836             Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
837             if (rnsd.isOK()) {
838                 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
839             } else if (rnsd.status==Result.ERR_Backend) {
840                 return Result.err(rnsd);
841             }
842             if (rnsd.isOK()) {
843                 return Result.ok(nsd);
844             } else if (rnsd.status==Result.ERR_Backend) {
845                 return Result.err(rnsd);
846             }
847         }
848         return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
849                 ns_and_type);
850     }
851
852     
853     /**
854      * isGranted
855      * 
856      * Important function - Check internal Permission Schemes for Permission to
857      * do things
858      * 
859      * @param trans
860      * @param type
861      * @param instance
862      * @param action
863      * @return
864      */
865     public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
866         Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
867         if (perms.isOK()) {
868             for (PermDAO.Data pd : perms.value) {
869                 if (ns.equals(pd.ns)) {
870                     if (type.equals(pd.type)) {
871                         if (PermEval.evalInstance(pd.instance, instance)) {
872                             if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action 
873                                 return true;
874                             }
875                         }
876                     }
877                 }
878             }
879         }
880         return false;
881     }
882
883     public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
884         Result<List<CredDAO.Data>> result;
885         TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
886         try {
887             result = credDAO.readIDBAth(trans, user);
888         } finally {
889             tt.done();
890         }
891
892         Result<Date> rv = null;
893         if (result.isOK()) {
894             if (result.isEmpty()) {
895                 rv = Result.err(Status.ERR_UserNotFound, user);
896                 if (willSpecialLog(trans,user)) {
897                     trans.audit().log("Special DEBUG:", user, " does not exist in DB");
898                 }
899             } else {
900                 Date now = new Date();
901                 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.     
902                 List<CredDAO.Data> cddl;
903                 if (result.value.size() > 1) {
904                     cddl = new ArrayList<>(result.value.size());
905                     for (CredDAO.Data old : result.value) {
906                         if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
907                             cddl.add(old);
908                         }
909                     }
910                     if (cddl.size()>1) {
911                         Collections.sort(cddl, (a, b) -> b.expires.compareTo(a.expires));
912                     }
913                 } else {
914                     cddl = result.value;
915                 }
916     
917                 Date expired = null;
918                 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
919                 for (CredDAO.Data cdd : cddl) {
920                     if (!cdd.id.equals(user)) {
921                         trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
922                     }
923                     if (cdd.expires.after(now)) {
924                         byte[] dbcred = cdd.cred.array();
925                         
926                         try {
927                             switch(cdd.type) {
928                                 case CredDAO.BASIC_AUTH:
929                                     byte[] md5=Hash.hashMD5(cred);
930                                     if (Hash.compareTo(md5,dbcred)==0) {
931                                         checkLessThanDays(trans,cldays,now,cdd);
932                                         trans.setTag(cdd.tag);
933                                         return Result.ok(cdd.expires);
934                                     } else if (debug!=null) {
935                                         load(debug, cdd);
936                                     }
937                                     break;
938                                 case CredDAO.BASIC_AUTH_SHA256:
939                                     ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
940                                     bb.putInt(cdd.other);
941                                     bb.put(cred);
942                                     byte[] hash = Hash.hashSHA256(bb.array());
943     
944                                     if (Hash.compareTo(hash,dbcred)==0) {
945                                         checkLessThanDays(trans,cldays,now,cdd);
946                                         trans.setTag(cdd.tag);
947                                         return Result.ok(cdd.expires);
948                                     } else if (debug!=null) {
949                                         load(debug, cdd);
950                                     }
951                                     break;
952                                 default:
953                                     trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
954                             }
955                         } catch (NoSuchAlgorithmException e) {
956                             trans.error().log(e);
957                         }
958                     } else {
959                         if (expired==null || expired.before(cdd.expires)) {
960                             expired = cdd.expires;
961                             trans.setTag(cdd.tag);
962                         }
963                     }
964                 } // end for each
965                 
966                 if (expired!=null) {
967                     // Note: this is only returned if there are no good Credentials
968                     rv = Result.err(Status.ERR_Security,
969                             "Credentials expired %s",Chrono.utcStamp(expired));
970                 } else {
971                     if (debug==null && alwaysSpecial) {
972                         debug = new StringBuilder();
973                     }
974                     if (debug!=null) {
975                         debug.append(trans.env().encryptor().encrypt(new String(cred)));
976                         rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
977                     }
978                 }
979             }
980         } else {
981             return Result.err(result);
982         }
983         return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
984     }
985
986
987     private void load(StringBuilder debug, Data cdd) {
988         debug.append("\nDB Entry: user=");
989         debug.append(cdd.id);
990         debug.append(",type=");
991         debug.append(cdd.type);
992         debug.append(",expires=");
993         debug.append(Chrono.dateTime(cdd.expires));
994         debug.append(",tag=");
995         debug.append(cdd.tag);
996         debug.append('\n');
997     }
998
999
1000     private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1001         long close = now.getTime() + (days * 86400000);
1002         long cexp=cdd.expires.getTime();
1003         if (cexp<close) {
1004             int daysLeft = days-(int)((close-cexp)/86400000);
1005             trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1006                 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag, 
1007                 daysLeft,daysLeft==1?"":"s");
1008         }
1009     }
1010
1011
1012     public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1013         if (cred.type==CredDAO.RAW) {
1014             TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1015             try {
1016                 cred.type = CredDAO.BASIC_AUTH_SHA256;
1017                 cred.other = random.nextInt();
1018                 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1019                 bb.putInt(cred.other);
1020                 bb.put(cred.cred);
1021                 byte[] hash = Hash.hashSHA256(bb.array());
1022                 cred.cred = ByteBuffer.wrap(hash);
1023                 return Result.ok(cred);
1024             } catch (NoSuchAlgorithmException e) {
1025                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1026             } finally {
1027                 tt.done();
1028             }
1029             
1030         } else if (cred.type==CredDAO.FQI) {
1031             cred.cred = null;
1032             return Result.ok(cred);
1033         }
1034         return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1035     }
1036     
1037     public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1038         Result<Boolean> rv;
1039         TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1040         try {
1041             switch(orig.type) {
1042                 case CredDAO.BASIC_AUTH_SHA256:
1043                     ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1044                     bb.putInt(orig.other);
1045                     bb.put(raw);
1046                     rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1047                 case CredDAO.BASIC_AUTH:
1048                     rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1049                 case CredDAO.FQI:
1050                 default:
1051                     rv = Result.ok(false);
1052             }
1053         } catch (NoSuchAlgorithmException e) {
1054             rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1055         } finally {
1056             tt.done();
1057         }
1058         return rv;
1059     }
1060
1061     public static final String APPROVED = "APPROVE";
1062     public static final String REJECT = "REJECT";
1063     public static final String PENDING = "PENDING";
1064
1065     public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1066             List<ApprovalDAO.Data> approvals) {
1067         // get the approval policy for the organization
1068
1069         // get the list of approvals with an accept status
1070
1071         // validate the approvals against the policy
1072
1073         // for now check if all approvals are received and return
1074         // SUCCESS/FAILURE/SKIP
1075         boolean bReject = false;
1076         boolean bPending = false;
1077
1078         for (ApprovalDAO.Data approval : approvals) {
1079             if (approval.status.equals(REJECT)) {
1080                 bReject = true;
1081             } else if (approval.status.equals(PENDING)) {
1082                 bPending = true;
1083             }
1084         }
1085         if (bReject) {
1086             return Result.err(Status.ERR_Policy,
1087                     "Approval Polocy not conformed");
1088         }
1089         if (bPending) {
1090             return Result.err(Status.ERR_ActionNotCompleted,
1091                     "Required Approvals not received");
1092         }
1093
1094         return Result.ok();
1095     }
1096
1097     private static final String NO_CACHE_NAME = "No Cache Data named %s";
1098
1099     public Result<Void> clearCache(AuthzTrans trans, String cname) {
1100         boolean all = "all".equals(cname);
1101         Result<Void> rv = null;
1102
1103         if (all || NsDAO.TABLE.equals(cname)) {
1104             int[] seg = series(NsDAO.CACHE_SEG);
1105             for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1106             rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1107         }
1108         if (all || PermDAO.TABLE.equals(cname)) {
1109             int[] seg = series(PermDAO.CACHE_SEG);
1110             for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1111             rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1112         }
1113         if (all || RoleDAO.TABLE.equals(cname)) {
1114             int[] seg = series(RoleDAO.CACHE_SEG);
1115             for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1116             rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1117         }
1118         if (all || UserRoleDAO.TABLE.equals(cname)) {
1119             int[] seg = series(UserRoleDAO.CACHE_SEG);
1120             for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1121             rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1122         }
1123         if (all || CredDAO.TABLE.equals(cname)) {
1124             int[] seg = series(CredDAO.CACHE_SEG);
1125             for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1126             rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1127         }
1128         if (all || CertDAO.TABLE.equals(cname)) {
1129             int[] seg = series(CertDAO.CACHE_SEG);
1130             for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1131             rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1132         }
1133
1134         if (rv == null) {
1135             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1136         }
1137         return rv;
1138     }
1139
1140     public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1141         Result<Void> rv;
1142         if (NsDAO.TABLE.equals(cname)) {
1143             rv = nsDAO.invalidate(segment);
1144         } else if (PermDAO.TABLE.equals(cname)) {
1145             rv = permDAO.invalidate(segment);
1146         } else if (RoleDAO.TABLE.equals(cname)) {
1147             rv = roleDAO.invalidate(segment);
1148         } else if (UserRoleDAO.TABLE.equals(cname)) {
1149             rv = userRoleDAO.invalidate(segment);
1150         } else if (CredDAO.TABLE.equals(cname)) {
1151             rv = credDAO.invalidate(segment);
1152         } else if (CertDAO.TABLE.equals(cname)) {
1153             rv = certDAO.invalidate(segment);
1154         } else {
1155             rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1156         }
1157         return rv;
1158     }
1159
1160     private int[] series(int max) {
1161         int[] series = new int[max];
1162         for (int i = 0; i < max; ++i)
1163             series[i] = i;
1164         return series;
1165     }
1166
1167     public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1168         Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1169         if (userDelegatedFor==null) {
1170             userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1171             rldd.put(user, userDelegatedFor);
1172         }
1173         if (userDelegatedFor.isOKhasData()) {
1174             for (DelegateDAO.Data curr : userDelegatedFor.value) {
1175                 if (curr.user.equals(approver) && curr.delegate.equals(user)
1176                         && curr.expires.after(new Date())) {
1177                     return true;
1178                 }
1179             }
1180         }
1181         return false;
1182     }
1183
1184     public static boolean willSpecialLog(AuthzTrans trans, String user) {
1185         Boolean b = trans.get(specialLogSlot, null);
1186         if (b==null) { // we haven't evaluated in this trans for Special Log yet
1187             if (specialLog==null) {
1188                 return false;
1189             } else {
1190                 b = specialLog.contains(user);
1191                 trans.put(specialLogSlot, b);
1192             }
1193         }
1194         return b;
1195     }
1196     
1197     public static void logEncryptTrace(AuthzTrans trans, String data) {
1198         long ti;
1199         trans.put(transIDSlot, ti=nextTraceID());
1200         trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1201     }
1202
1203     private synchronized static long nextTraceID() {
1204         return ++traceID;
1205     }
1206
1207     public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1208         if (specialLog == null) {
1209             specialLog = new HashSet<>();
1210         }
1211         boolean rc = specialLog.add(id);
1212         if (rc) {
1213             trans.trace().printf("Trace on for %s requested by %s",id,trans.user());            
1214         }
1215         return rc;
1216     }
1217
1218     public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1219         if (specialLog==null) {
1220             return false;
1221         }
1222         boolean rv = specialLog.remove(id);
1223         if (specialLog.isEmpty()) {
1224             specialLog = null;
1225         }
1226         if (rv) {
1227             trans.trace().printf("Trace off for %s requested by %s",id,trans.user());            
1228         }
1229         return rv;
1230     }
1231
1232     /** 
1233      * canMove
1234      * Which Types can be moved
1235      * @param nsType
1236      * @return
1237      */
1238     public boolean canMove(NsType nsType) {
1239         boolean rv;
1240         switch(nsType) {
1241             case DOT:
1242             case ROOT:
1243             case COMPANY:
1244             case UNKNOWN:
1245                 rv = false;
1246                 break;
1247             default:
1248                 rv = true;
1249         }
1250         return rv;
1251     }
1252
1253     public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1254         Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1255         if (rur.isOKhasData()) {
1256             Date now = new Date();
1257             for (UserRoleDAO.Data urdd : rur.value){
1258                 if (urdd.expires.after(now)) {
1259                     return true;
1260                 }
1261             }
1262         };
1263         return false;
1264     }
1265     
1266     public boolean isOwner(AuthzTrans trans, String user, String ns) {
1267         Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1268         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1269             Date now = new Date();
1270             if (urdd.expires.after(now)) {
1271                 return true;
1272             }
1273         }};
1274         return false;
1275     }
1276
1277     public int countOwner(AuthzTrans trans, String ns) {
1278         Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1279         Date now = new Date();
1280         int count = 0;
1281         if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1282             if (urdd.expires.after(now)) {
1283                 ++count;
1284             }
1285         }};
1286         return count;
1287     }
1288     
1289     /**
1290      * Return a Unique String, (same string, if it is already unique), with only
1291      * lowercase letters, digits and the '.' character.
1292      * 
1293      * @param name
1294      * @return
1295      * @throws IOException 
1296      */
1297     public static String toUnique(String name) throws IOException {
1298         byte[] from = name.getBytes();
1299         StringBuilder sb = new StringBuilder();
1300         byte f;
1301         for (int i=0;i<from.length;++i) {
1302             f=(byte)(from[i]); // printables;
1303             sb.append((char)((f>>4)+0x61));
1304             sb.append((char)((f&0x0F)+0x61));
1305         }
1306         return sb.toString();
1307     }
1308     
1309     public static String fromUnique(String name) throws IOException {
1310         byte[] from = name.getBytes();
1311         StringBuilder sb = new StringBuilder();
1312         char c;
1313         for (int i=0;i<from.length;++i) {
1314             c = (char)((from[i]-0x61)<<4);
1315             c |= (from[++i]-0x61);
1316             sb.append(c);
1317         }
1318         return sb.toString();
1319     }
1320
1321 }