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