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