AT&T 2.0.19 Code drop, stage 3
[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.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.dao.cass.CredDAO.Data;
65 import org.onap.aaf.auth.env.AuthzTrans;
66 import org.onap.aaf.auth.env.AuthzTransFilter;
67 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
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.ROOT_NS();
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<String>();
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<PermDAO.Data>();
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<String>();
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<PermDAO.Data>();
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 "com.att" 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 "com.att"
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 = child == null ? -1 : child.lastIndexOf('.');
371                         if (dot < 0) {
372                                 return Result.err(Status.ERR_NsNotFound,
373                                                 "No Namespace for [%s]", child);
374                         } else {
375                                 return deriveNs(trans, child.substring(0, dot));
376                         }
377                 }
378         }
379
380         public Result<NsDAO.Data> deriveFirstNsForType(AuthzTrans trans, String str, NsType type) {
381                 NsDAO.Data nsd;
382
383                 for(String lookup = str;!".".equals(lookup) && lookup!=null;) {
384                         Result<List<NsDAO.Data>> rld = nsDAO.read(trans, lookup);
385                         if(rld.isOKhasData()) {
386                                 nsd=rld.value.get(0);
387                                 lookup = nsd.parent;
388                                 if(type.type == nsd.type) {
389                                         return Result.ok(nsd);
390                                 }
391                         } else {
392                                 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
393                         }
394                 }
395                 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
396         }
397
398         public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
399                 Result<NsDAO.Data> ndd = deriveNs(trans, child);
400                 if (ndd.isOK()) {
401                         NsSplit nss = new NsSplit(ndd.value, child);
402                         if (nss.isOK()) {
403                                 return Result.ok(nss);
404                         } else {
405                                 return Result.err(Status.ERR_NsNotFound,
406                                                 "Cannot split [%s] into valid namespace elements",
407                                                 child);
408                         }
409                 }
410                 return Result.err(ndd);
411         }
412
413         /**
414          * Translate an ID into it's domain
415          * 
416          * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
417          * 
418          * @param id
419          * @return
420          */
421         public static String domain2ns(String id) {
422                 int at = id.indexOf('@');
423                 if (at >= 0) {
424                         String[] domain = id.substring(at + 1).split("\\.");
425                         StringBuilder ns = new StringBuilder(id.length());
426                         boolean first = true;
427                         for (int i = domain.length - 1; i >= 0; --i) {
428                                 if (first) {
429                                         first = false;
430                                 } else {
431                                         ns.append('.');
432                                 }
433                                 ns.append(domain[i]);
434                         }
435                         return ns.toString();
436                 } else {
437                         return "";
438                 }
439
440         }
441
442         /**
443          * Validate Namespace of ID@Domain
444          * 
445          * Namespace is reverse order of Domain.
446          * 
447          * @param trans
448          * @param id
449          * @return
450          */
451         public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
452                 // Take domain, reverse order, and check on NS
453                 String ns;
454                 if(id.indexOf('@')<0) { // it's already an ns, not an ID
455                         ns = id;
456                 } else {
457                         ns = domain2ns(id);
458                 }
459                 if (ns.length() > 0) {
460                         if(!trans.org().getDomain().equals(ns)) { 
461                                 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
462                                 if (rlnsd.isOKhasData()) {
463                                         return Result.ok(rlnsd.value.get(0));
464                                 }
465                         }
466                 }
467                 return Result.err(Status.ERR_NsNotFound,
468                                 "A Namespace is not available for %s", id);
469         }
470
471         public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
472                 // <ns>.access|:role:<role name>|<read|write>
473                 String ns = ndd.name;
474                 int last;
475                 do {
476                         if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
477                                 return Result.ok(ndd);
478                         }
479                         if ((last = ns.lastIndexOf('.')) >= 0) {
480                                 ns = ns.substring(0, last);
481                         }
482                 } while (last >= 0);
483                 // com.att.aaf.ns|:<client ns>:ns|<access>
484                 // AAF-724 - Make consistent response for May User", and not take the
485                 // last check... too confusing.
486                 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
487                 if (rv.isOK()) {
488                         return rv;
489                 } else if(rv.status==Result.ERR_Backend) {
490                         return Result.err(rv);
491                 } else {
492                         return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
493                                         user, access.name(), ndd.name);
494                 }
495         }
496
497         public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
498                 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
499                 if (rnsd.isOK()) {
500                         return mayUser(trans, user, rnsd.value, rdd, access);
501                 }
502                 return rnsd;
503         }
504
505         public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
506                 // 1) Is User in the Role?
507                 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
508                 if (rurd.isOKhasData()) {
509                         return Result.ok(ndd);
510                 }
511
512                 String roleInst = ":role:" + rdd.name;
513                 // <ns>.access|:role:<role name>|<read|write>
514                 String ns = rdd.ns;
515                 int last;
516                 do {
517                         if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
518                                 return Result.ok(ndd);
519                         }
520                         if ((last = ns.lastIndexOf('.')) >= 0) {
521                                 ns = ns.substring(0, last);
522                         }
523                 } while (last >= 0);
524
525                 // Check if Access by Global Role perm
526                 // com.att.aaf.ns|:<client ns>:role:name|<access>
527                 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
528                                 + rdd.ns + roleInst, access.name());
529                 if (rnsd.isOK()) {
530                         return rnsd;
531                 } else if(rnsd.status==Result.ERR_Backend) {
532                         return Result.err(rnsd);
533                 }
534
535                 // Check if Access to Whole NS
536                 // AAF-724 - Make consistent response for May User", and not take the
537                 // last check... too confusing.
538                 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, 
539                                 ":" + rdd.ns + ":ns", access.name());
540                 if (rv.isOK()) {
541                         return rv;
542                 } else if(rnsd.status==Result.ERR_Backend) {
543                         return Result.err(rnsd);
544                 } else {
545                         return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
546                                         user, access.name(), rdd.fullName());
547                 }
548
549         }
550
551         public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
552                 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
553                 if (rnsd.isOK()) {
554                         return mayUser(trans, user, rnsd.value, pdd, access);
555                 }
556                 return rnsd;
557         }
558
559         public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
560                 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
561                         return Result.ok(ndd);
562                 }
563                 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
564                 // <ns>.access|:role:<role name>|<read|write>
565                 String ns = ndd.name;
566                 int last;
567                 do {
568                         if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
569                                 return Result.ok(ndd);
570                         }
571                         if ((last = ns.lastIndexOf('.')) >= 0) {
572                                 ns = ns.substring(0, last);
573                         }
574                 } while (last >= 0);
575
576                 // Check if Access by NS perm
577                 // com.att.aaf.ns|:<client ns>:role:name|<access>
578                 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
579                 if (rnsd.isOK()) {
580                         return rnsd;
581                 } else if(rnsd.status==Result.ERR_Backend) {
582                         return Result.err(rnsd);
583                 }
584
585                 // Check if Access to Whole NS
586                 // AAF-724 - Make consistent response for May User", and not take the
587                 // last check... too confusing.
588                 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
589                 if (rv.isOK()) {
590                         return rv;
591                 } else {
592                         return Result.err(Status.ERR_Denied,
593                                         "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
594                                         pdd.fullType(), pdd.instance, pdd.action);
595                 }
596
597         }
598
599         public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
600                 try {
601                         Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
602                         if(rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":"   + rnsd.value.name + ":ns", access.name()).isOK()) {
603                                 return Result.ok();
604                         }
605                         boolean isUser = trans.user().equals(dd.user);
606                         boolean isDelegate = dd.delegate != null
607                                         && (dd.user.equals(dd.delegate) || trans.user().equals(
608                                                         dd.delegate));
609                         Organization org = trans.org();
610                         switch (access) {
611                         case create:
612                                 if (org.getIdentity(trans, dd.user) == null) {
613                                         return Result.err(Status.ERR_UserNotFound,
614                                                         "[%s] is not a user in the company database.",
615                                                         dd.user);
616                                 }
617                                 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
618                                         return Result.err(Status.ERR_UserNotFound,
619                                                         "[%s] is not a user in the company database.",
620                                                         dd.delegate);
621                                 }
622                                 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
623                                         return Result.err(Status.ERR_BadData,
624                                                         "[%s] cannot be a delegate for self", dd.user);
625                                 }
626                                 if (!isUser     && !isGranted(trans, trans.user(), ROOT_NS,DELG,
627                                                                 org.getDomain(), Question.CREATE)) {
628                                         return Result.err(Status.ERR_Denied,
629                                                         "[%s] may not create a delegate for [%s]",
630                                                         trans.user(), dd.user);
631                                 }
632                                 break;
633                         case read:
634                         case write:
635                                 if (!isUser     && !isDelegate && 
636                                                 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
637                                         return Result.err(Status.ERR_Denied,
638                                                         "[%s] may not %s delegates for [%s]", trans.user(),
639                                                         access.name(), dd.user);
640                                 }
641                                 break;
642                         default:
643                                 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
644                         }
645                 } catch (Exception e) {
646                         return Result.err(e);
647                 }
648                 return Result.ok();
649         }
650
651         /*
652          * Check (recursively, if necessary), if able to do something based on NS
653          */
654         private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user,     NsDAO.Data nsd, String ns_and_type, String access) {
655                 String ns = nsd.name;
656
657                 // If an ADMIN of the Namespace, then allow
658                 
659                 Result<List<UserRoleDAO.Data>> rurd;
660                 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
661                         return Result.ok(nsd);
662                 } else if(rurd.status==Result.ERR_Backend) {
663                         return Result.err(rurd);
664                 }
665                 
666                 // If Specially granted Global Permission
667                 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
668                         return Result.ok(nsd);
669                 }
670
671                 // Check recur
672
673                 int dot = ns.length();
674                 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
675                         Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
676                         if (rnsd.isOK()) {
677                                 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
678                         } else if(rnsd.status==Result.ERR_Backend) {
679                                 return Result.err(rnsd);
680                         }
681                         if (rnsd.isOK()) {
682                                 return Result.ok(nsd);
683                         } else if(rnsd.status==Result.ERR_Backend) {
684                                 return Result.err(rnsd);
685                         }
686                 }
687                 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
688                                 ns_and_type);
689         }
690
691         
692         /**
693          * isGranted
694          * 
695          * Important function - Check internal Permission Schemes for Permission to
696          * do things
697          * 
698          * @param trans
699          * @param type
700          * @param instance
701          * @param action
702          * @return
703          */
704         public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
705                 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
706                 if (perms.isOK()) {
707                         for (PermDAO.Data pd : perms.value) {
708                                 if (ns.equals(pd.ns)) {
709                                         if (type.equals(pd.type)) {
710                                                 if (PermEval.evalInstance(pd.instance, instance)) {
711                                                         if(PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action 
712                                                                 return true;
713                                                         }
714                                                 }
715                                         }
716                                 }
717                         }
718                 }
719                 return false;
720         }
721
722         public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
723                 Result<List<CredDAO.Data>> result;
724                 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
725                 try {
726                         result = credDAO.readID(trans, user);
727                 } finally {
728                         tt.done();
729                 }
730
731                 Result<Date> rv = null;
732                 if(result.isOK()) {
733                         if (result.isEmpty()) {
734                                 rv = Result.err(Status.ERR_UserNotFound, user);
735                                 if (willSpecialLog(trans,user)) {
736                                         trans.audit().log("Special DEBUG:", user, " does not exist in DB");
737                                 }
738                         } else {
739                                 Date now = new Date();//long now = System.currentTimeMillis();
740                                 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.         
741                                 List<CredDAO.Data> cddl;
742                                 if(result.value.size() > 1) {
743                                         cddl = new ArrayList<CredDAO.Data>(result.value.size());
744                                         for(CredDAO.Data old : result.value) {
745                                                 if(old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
746                                                         cddl.add(old);
747                                                 }
748                                         }
749                                         if(cddl.size()>1) {
750                                                 Collections.sort(cddl,new Comparator<CredDAO.Data>() {
751                                                         @Override
752                                                         public int compare(org.onap.aaf.auth.dao.cass.CredDAO.Data a,
753                                                                                            org.onap.aaf.auth.dao.cass.CredDAO.Data b) {
754                                                                 return b.expires.compareTo(a.expires);
755                                                         }
756                                                 });
757                                         }
758                                 } else {
759                                         cddl = result.value;
760                                 }
761         
762                                 Date expired = null;
763                                 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
764                                 for (CredDAO.Data cdd : cddl) {
765                                         if(!cdd.id.equals(user)) {
766                                                 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
767                                         }
768                                         if (cdd.expires.after(now)) {
769                                                 byte[] dbcred = cdd.cred.array();
770                                                 
771                                                 try {
772                                                         switch(cdd.type) {
773                                                                 case CredDAO.BASIC_AUTH:
774                                                                         byte[] md5=Hash.hashMD5(cred);
775                                                                         if(Hash.compareTo(md5,dbcred)==0) {
776                                                                                 checkLessThanDays(trans,7,now,cdd);
777                                                                                 return Result.ok(cdd.expires);
778                                                                         } else if (debug!=null) {
779                                                                                 load(debug, cdd,dbcred);
780                                                                         }
781                                                                         break;
782                                                                 case CredDAO.BASIC_AUTH_SHA256:
783                                                                         ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
784                                                                         bb.putInt(cdd.other);
785                                                                         bb.put(cred);
786                                                                         byte[] hash = Hash.hashSHA256(bb.array());
787         
788                                                                         if(Hash.compareTo(hash,dbcred)==0) {
789                                                                                 checkLessThanDays(trans,7,now,cdd);
790                                                                                 return Result.ok(cdd.expires);
791                                                                         } else if (debug!=null) {
792                                                                                 load(debug, cdd, dbcred);
793                                                                         }
794                                                                         break;
795                                                                 default:
796                                                                         trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
797                                                         }
798                                                 } catch (NoSuchAlgorithmException e) {
799                                                         trans.error().log(e);
800                                                 }
801                                         } else {
802                                                 if(expired==null || expired.before(cdd.expires)) {
803                                                         expired = cdd.expires;
804                                                 }
805                                         }
806                                 } // end for each
807                                 if(debug==null) {
808                                         debug=new StringBuilder();
809                                 } else {
810                                         debug.append(", ");
811                                 }
812                                 
813                                 debug.append("cred=");
814                                 debug.append(new String(cred));
815                                 trans.audit().printf("No cred matches ip=%s, user=%s, %s\n",trans.ip(),user,trans.encryptor().encrypt(debug.toString()));
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, byte[] dbcred) {
830                 debug.append("DB Entry: user=");
831                 debug.append(cdd.id);
832                 debug.append(",type=");
833                 debug.append(cdd.type);
834                 debug.append(",cred=");
835                 debug.append(Hash.toHex(dbcred));
836                 debug.append(",expires=");
837                 debug.append(Chrono.dateTime(cdd.expires));
838                 debug.append('\n');
839         }
840
841
842         private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
843                 long close = now.getTime() + (days * 86400000);
844                 long cexp=cdd.expires.getTime();
845                 if(cexp<close) {
846                         int daysLeft = days-(int)((close-cexp)/86400000);
847                         trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,msg=\"Password expires in less than %d day%s\"",
848                                 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, daysLeft,daysLeft==1?"":"s");
849                 }
850         }
851
852
853         public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
854                 if(cred.type==CredDAO.RAW) {
855                         TimeTaken tt = trans.start("Hash Cred", Env.SUB);
856                         try {
857                                 cred.type = CredDAO.BASIC_AUTH_SHA256;
858                                 cred.other = random.nextInt();
859                                 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
860                                 bb.putInt(cred.other);
861                                 bb.put(cred.cred);
862                                 byte[] hash = Hash.hashSHA256(bb.array());
863                                 cred.cred = ByteBuffer.wrap(hash);
864                                 return Result.ok(cred);
865                         } catch (NoSuchAlgorithmException e) {
866                                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
867                         } finally {
868                                 tt.done();
869                         }
870                         
871                 }
872                 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
873         }
874         
875         public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
876                         TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
877                         try {
878                                 switch(orig.type) {
879                                         case CredDAO.BASIC_AUTH_SHA256:
880                                                 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
881                                                 bb.putInt(orig.other);
882                                                 bb.put(raw);
883                                                 return Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
884                                         case CredDAO.BASIC_AUTH:
885                                                 return Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
886                                         default:
887                                                 return Result.ok(false);
888                                 }
889                         } catch (NoSuchAlgorithmException e) {
890                                 return Result.err(Status.ERR_General,e.getLocalizedMessage());
891                         } finally {
892                                 tt.done();
893                         }
894         }
895
896         public static final String APPROVED = "APPROVE";
897         public static final String REJECT = "REJECT";
898         public static final String PENDING = "PENDING";
899
900         public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
901                         List<ApprovalDAO.Data> approvals) {
902                 // get the approval policy for the organization
903
904                 // get the list of approvals with an accept status
905
906                 // validate the approvals against the policy
907
908                 // for now check if all approvals are received and return
909                 // SUCCESS/FAILURE/SKIP
910                 boolean bReject = false;
911                 boolean bPending = false;
912
913                 for (ApprovalDAO.Data approval : approvals) {
914                         if (approval.status.equals(REJECT)) {
915                                 bReject = true;
916                         } else if (approval.status.equals(PENDING)) {
917                                 bPending = true;
918                         }
919                 }
920                 if (bReject) {
921                         return Result.err(Status.ERR_Policy,
922                                         "Approval Polocy not conformed");
923                 }
924                 if (bPending) {
925                         return Result.err(Status.ERR_ActionNotCompleted,
926                                         "Required Approvals not received");
927                 }
928
929                 return Result.ok();
930         }
931
932         private static final String NO_CACHE_NAME = "No Cache Data named %s";
933
934         public Result<Void> clearCache(AuthzTrans trans, String cname) {
935                 boolean all = "all".equals(cname);
936                 Result<Void> rv = null;
937
938                 if (all || NsDAO.TABLE.equals(cname)) {
939                         int seg[] = series(NsDAO.CACHE_SEG);
940                         for(int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
941                         rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
942                 }
943                 if (all || PermDAO.TABLE.equals(cname)) {
944                         int seg[] = series(NsDAO.CACHE_SEG);
945                         for(int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
946                         rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
947                 }
948                 if (all || RoleDAO.TABLE.equals(cname)) {
949                         int seg[] = series(NsDAO.CACHE_SEG);
950                         for(int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
951                         rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
952                 }
953                 if (all || UserRoleDAO.TABLE.equals(cname)) {
954                         int seg[] = series(NsDAO.CACHE_SEG);
955                         for(int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
956                         rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
957                 }
958                 if (all || CredDAO.TABLE.equals(cname)) {
959                         int seg[] = series(NsDAO.CACHE_SEG);
960                         for(int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
961                         rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
962                 }
963                 if (all || CertDAO.TABLE.equals(cname)) {
964                         int seg[] = series(NsDAO.CACHE_SEG);
965                         for(int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
966                         rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
967                 }
968
969                 if (rv == null) {
970                         rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
971                 }
972                 return rv;
973         }
974
975         public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
976                 Result<Void> rv;
977                 if (NsDAO.TABLE.equals(cname)) {
978                         rv = nsDAO.invalidate(segment);
979                 } else if (PermDAO.TABLE.equals(cname)) {
980                         rv = permDAO.invalidate(segment);
981                 } else if (RoleDAO.TABLE.equals(cname)) {
982                         rv = roleDAO.invalidate(segment);
983                 } else if (UserRoleDAO.TABLE.equals(cname)) {
984                         rv = userRoleDAO.invalidate(segment);
985                 } else if (CredDAO.TABLE.equals(cname)) {
986                         rv = credDAO.invalidate(segment);
987                 } else if (CertDAO.TABLE.equals(cname)) {
988                         rv = certDAO.invalidate(segment);
989                 } else {
990                         rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
991                 }
992                 return rv;
993         }
994
995         private int[] series(int max) {
996                 int[] series = new int[max];
997                 for (int i = 0; i < max; ++i)
998                         series[i] = i;
999                 return series;
1000         }
1001
1002         public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1003                 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1004                 if(userDelegatedFor==null) {
1005                         userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1006                         rldd.put(user, userDelegatedFor);
1007                 }
1008                 if(userDelegatedFor.isOKhasData()) {
1009                         for (DelegateDAO.Data curr : userDelegatedFor.value) {
1010                                 if (curr.user.equals(approver) && curr.delegate.equals(user)
1011                                                 && curr.expires.after(new Date())) {
1012                                         return true;
1013                                 }
1014                         }
1015                 }
1016                 return false;
1017         }
1018
1019         public static boolean willSpecialLog(AuthzTrans trans, String user) {
1020                 Boolean b = trans.get(specialLogSlot, null);
1021                 if(b==null) { // we haven't evaluated in this trans for Special Log yet
1022                         if(specialLog==null) {
1023                                 return false;
1024                         } else {
1025                                 b = specialLog.contains(user);
1026                                 trans.put(specialLogSlot, b);
1027                         }
1028                 }
1029                 return b;
1030         }
1031         
1032         public static void logEncryptTrace(AuthzTrans trans, String data) {
1033                 long ti;
1034                 trans.put(transIDSlot, ti=nextTraceID());
1035                 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1036         }
1037
1038         private synchronized static long nextTraceID() {
1039                 return ++traceID;
1040         }
1041
1042         public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1043                 if (specialLog == null) {
1044                         specialLog = new HashSet<String>();
1045                 }
1046                 boolean rc = specialLog.add(id);
1047                 if(rc) {
1048                         trans.trace().printf("Trace on for %s requested by %s",id,trans.user());                        
1049                 }
1050                 return rc;
1051         }
1052
1053         public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1054                 if(specialLog==null) {
1055                         return false;
1056                 }
1057                 boolean rv = specialLog.remove(id);
1058                 if (specialLog.isEmpty()) {
1059                         specialLog = null;
1060                 }
1061                 if(rv) {
1062                         trans.trace().printf("Trace off for %s requested by %s",id,trans.user());                       
1063                 }
1064                 return rv;
1065         }
1066
1067         /** 
1068          * canMove
1069          * Which Types can be moved
1070          * @param nsType
1071          * @return
1072          */
1073         public boolean canMove(NsType nsType) {
1074                 boolean rv;
1075                 switch(nsType) {
1076                         case DOT:
1077                         case ROOT:
1078                         case COMPANY:
1079                         case UNKNOWN:
1080                                 rv = false;
1081                                 break;
1082                         default:
1083                                 rv = true;
1084                 }
1085                 return rv;
1086         }
1087
1088         public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1089                 Date now = new Date();
1090                 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1091                 if(rur.isOKhasData()) {for(UserRoleDAO.Data urdd : rur.value){
1092                         if(urdd.expires.after(now)) {
1093                                 return true;
1094                         }
1095                 }};
1096                 return false;
1097         }
1098         
1099         public boolean isOwner(AuthzTrans trans, String user, String ns) {
1100                 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_OWNER);
1101                 Date now = new Date();
1102                 if(rur.isOKhasData()) {for(UserRoleDAO.Data urdd : rur.value){
1103                         if(urdd.expires.after(now)) {
1104                                 return true;
1105                         }
1106                 }};
1107                 return false;
1108         }
1109
1110         public int countOwner(AuthzTrans trans, String ns) {
1111                 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.readByRole(trans,ns+DOT_OWNER);
1112                 Date now = new Date();
1113                 int count = 0;
1114                 if(rur.isOKhasData()) {for(UserRoleDAO.Data urdd : rur.value){
1115                         if(urdd.expires.after(now)) {
1116                                 ++count;
1117                         }
1118                 }};
1119                 return count;
1120         }
1121         
1122         /**
1123          * Return a Unique String, (same string, if it is already unique), with only
1124          * lowercase letters, digits and the '.' character.
1125          * 
1126          * @param name
1127          * @return
1128          * @throws IOException 
1129          */
1130         public static String toUnique(String name) throws IOException {
1131                 byte[] from = name.getBytes();
1132                 StringBuilder sb = new StringBuilder();
1133                 byte f;
1134                 for(int i=0;i<from.length;++i) {
1135                         f=(byte)(from[i]); // printables;
1136                         sb.append((char)((f>>4)+0x61));
1137                         sb.append((char)((f&0x0F)+0x61));
1138                 }
1139                 return sb.toString();
1140         }
1141         
1142         public static String fromUnique(String name) throws IOException {
1143                 byte[] from = name.getBytes();
1144                 StringBuilder sb = new StringBuilder();
1145                 char c;
1146                 for(int i=0;i<from.length;++i) {
1147                         c = (char)((from[i]-0x61)<<4);
1148                         c |= (from[++i]-0x61);
1149                         sb.append(c);
1150                 }
1151                 return sb.toString();
1152         }
1153
1154 }