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