2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.auth.dao.hl;
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.Collection;
30 import java.util.Date;
31 import java.util.HashSet;
32 import java.util.List;
35 import java.util.TreeMap;
36 import java.util.TreeSet;
38 import org.onap.aaf.auth.common.Define;
39 import org.onap.aaf.auth.dao.AbsCassDAO;
40 import org.onap.aaf.auth.dao.CachedDAO;
41 import org.onap.aaf.auth.dao.DAOException;
42 import org.onap.aaf.auth.dao.cached.CachedCertDAO;
43 import org.onap.aaf.auth.dao.cached.CachedCredDAO;
44 import org.onap.aaf.auth.dao.cached.CachedNSDAO;
45 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
46 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
47 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
48 import org.onap.aaf.auth.dao.cached.FileGetter;
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.AuthzEnv;
66 import org.onap.aaf.auth.env.AuthzTrans;
67 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
68 import org.onap.aaf.auth.env.AuthzTransFilter;
69 import org.onap.aaf.auth.layer.Result;
70 import org.onap.aaf.auth.org.Organization;
71 import org.onap.aaf.cadi.Hash;
72 import org.onap.aaf.cadi.aaf.PermEval;
73 import org.onap.aaf.cadi.config.Config;
74 import org.onap.aaf.misc.env.APIException;
75 import org.onap.aaf.misc.env.Env;
76 import org.onap.aaf.misc.env.Slot;
77 import org.onap.aaf.misc.env.TimeTaken;
78 import org.onap.aaf.misc.env.util.Chrono;
80 import com.datastax.driver.core.Cluster;
85 * A Data Access Combination Object which asks Security and other Questions
90 public class Question {
92 // DON'T CHANGE FROM lower Case!!!
97 public static final String OWNER="owner";
98 public static final String ADMIN="admin";
99 public static final String DOT_OWNER=".owner";
100 public static final String DOT_ADMIN=".admin";
101 public static final String ACCESS = "access";
103 static final String ASTERIX = "*";
109 public static final String READ = Access.read.name();
110 public static final String WRITE = Access.write.name();
111 public static final String CREATE = Access.create.name();
113 public static final String ROLE = Type.role.name();
114 public static final String PERM = Type.perm.name();
115 public static final String NS = Type.ns.name();
116 public static final String CRED = Type.cred.name();
117 private static final String DELG = "delg";
118 public static final String ROOT_NS = Define.isInitialized() ? Define.ROOT_NS() : "undefined";
119 public static final String ATTRIB = "attrib";
122 public static final int MAX_SCOPE = 10;
123 public static final int APP_SCOPE = 3;
124 public static final int COMPANY_SCOPE = 2;
127 private static Set<String> specialLog = null;
128 public static final SecureRandom random = new SecureRandom();
129 private static long traceID = random.nextLong();
130 private static Slot specialLogSlot = null;
131 private static Slot transIDSlot = null;
134 private final HistoryDAO historyDAO;
135 public HistoryDAO historyDAO() {
139 private final CachedNSDAO nsDAO;
140 public CachedNSDAO nsDAO() {
144 private final CachedRoleDAO roleDAO;
145 public CachedRoleDAO roleDAO() {
149 private final CachedPermDAO permDAO;
150 public CachedPermDAO permDAO() {
154 private final CachedUserRoleDAO userRoleDAO;
155 public CachedUserRoleDAO userRoleDAO() {
159 private final CachedCredDAO credDAO;
160 public CachedCredDAO credDAO() {
164 private final CachedCertDAO certDAO;
165 public CachedCertDAO certDAO() {
169 private final DelegateDAO delegateDAO;
170 public DelegateDAO delegateDAO() {
174 private final FutureDAO futureDAO;
175 public FutureDAO futureDAO() {
179 private final ApprovalDAO approvalDAO;
180 public ApprovalDAO approvalDAO() {
184 public final LocateDAO locateDAO;
185 public LocateDAO locateDAO() {
189 private final CacheInfoDAO cacheInfoDAO;
190 private final int cldays;
191 private final boolean alwaysSpecial;
193 public Question(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
194 PERMS = trans.slot("USER_PERMS");
195 trans.init().log("Instantiating DAOs");
196 long expiresIn = Long.parseLong(trans.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF));
197 historyDAO = new HistoryDAO(trans, cluster, keyspace);
199 // Deal with Cached Entries
200 cacheInfoDAO = new CacheInfoDAO(trans, historyDAO);
202 nsDAO = new CachedNSDAO(new NsDAO(trans, historyDAO, cacheInfoDAO),cacheInfoDAO, expiresIn);
203 permDAO = new CachedPermDAO(new PermDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
204 roleDAO = new CachedRoleDAO(new RoleDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
205 userRoleDAO = new CachedUserRoleDAO(new UserRoleDAO(trans, historyDAO,cacheInfoDAO), cacheInfoDAO, expiresIn);
206 // Create if aaf_file_cred exists with file
207 FileGetter.singleton(trans.env().access());
208 credDAO = new CachedCredDAO(new CredDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
209 certDAO = new CachedCertDAO(new CertDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
211 locateDAO = new LocateDAO(trans,historyDAO);
212 futureDAO = new FutureDAO(trans, historyDAO);
213 delegateDAO = new DelegateDAO(trans, historyDAO);
214 approvalDAO = new ApprovalDAO(trans, historyDAO);
216 if (specialLogSlot==null) {
217 specialLogSlot = trans.slot(AuthzTransFilter.SPECIAL_LOG_SLOT);
220 if (transIDSlot==null) {
221 transIDSlot = trans.slot(AuthzTransFilter.TRANS_ID_SLOT);
224 AbsCassDAO.primePSIs(trans);
226 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
228 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
232 * Note: This Constructor created for JUNIT Purposes. Do not use otherwise.
234 public Question(AuthzTrans trans, HistoryDAO historyDAO, CacheInfoDAO cacheInfoDAO,
235 CachedNSDAO nsDAO, CachedPermDAO permDAO, CachedRoleDAO roleDAO,
236 CachedUserRoleDAO userRoleDAO, CachedCredDAO credDAO, CachedCertDAO certDAO,
237 LocateDAO locateDAO,FutureDAO futureDAO, DelegateDAO delegateDAO,
238 ApprovalDAO approvalDAO ) {
239 this.historyDAO = historyDAO;
240 this.cacheInfoDAO = cacheInfoDAO;
242 this.permDAO = permDAO;
243 this.roleDAO = roleDAO;
244 this.userRoleDAO = userRoleDAO;
245 this.credDAO = credDAO;
246 this.certDAO = certDAO;
247 this.locateDAO = locateDAO;
248 this.futureDAO = futureDAO;
249 this.delegateDAO = delegateDAO;
250 this.approvalDAO = approvalDAO;
252 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
253 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
256 public void startTimers(AuthzEnv env) {
257 // Only want to aggressively cleanse User related Caches... The others,
258 // just normal refresh
259 CachedDAO.startCleansing(env, credDAO, userRoleDAO);
260 CachedDAO.startRefresh(env, cacheInfoDAO);
263 public void close(AuthzTrans trans) {
264 historyDAO.close(trans);
265 cacheInfoDAO.close(trans);
267 permDAO.close(trans);
268 roleDAO.close(trans);
269 userRoleDAO.close(trans);
270 credDAO.close(trans);
271 certDAO.close(trans);
272 delegateDAO.close(trans);
273 futureDAO.close(trans);
274 approvalDAO.close(trans);
277 public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type, String instance, String action) {
278 if(type.indexOf('@') >= 0) {
279 int colon = type.indexOf(':');
281 PermDAO.Data pdd = new PermDAO.Data();
282 pdd.ns = type.substring(0, colon);
283 pdd.type = type.substring(colon+1);
284 pdd.instance = instance;
287 return Result.ok(pdd);
289 return Result.err(Result.ERR_BadData,"Could not extract ns and type from " + type);
292 Result<NsDAO.Data> rnd = deriveNs(trans, type);
294 return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
297 return Result.err(rnd);
305 * Because this call is frequently called internally, AND because we already
306 * look for it in the initial Call, we cache within the Transaction
312 public Result<List<PermDAO.Data>> getPermsByUser(AuthzTrans trans, String user, boolean lookup) {
313 return PermLookup.get(trans, this, user).getPerms(lookup);
316 public Result<List<PermDAO.Data>> getPermsByUserFromRolesFilter(AuthzTrans trans, String user, String forUser) {
317 PermLookup plUser = PermLookup.get(trans, this, user);
318 Result<Set<String>> plPermNames = plUser.getPermNames();
319 if (plPermNames.notOK()) {
320 return Result.err(plPermNames);
324 if (forUser.equals(user)) {
327 // Setup a TreeSet to check on Namespaces to
328 nss = new TreeSet<>();
329 PermLookup fUser = PermLookup.get(trans, this, forUser);
330 Result<Set<String>> forUpn = fUser.getPermNames();
331 if (forUpn.notOK()) {
332 return Result.err(forUpn);
335 for (String pn : forUpn.value) {
336 Result<String[]> decoded = PermDAO.Data.decodeToArray(trans, this, pn);
337 if (decoded.isOKhasData()) {
338 nss.add(decoded.value[0]);
340 trans.error().log(pn,", derived from a Role, is invalid:",decoded.errorString());
345 List<PermDAO.Data> rlpUser = new ArrayList<>();
346 Result<PermDAO.Data> rpdd;
348 for (String pn : plPermNames.value) {
349 rpdd = PermDAO.Data.decode(trans, this, pn);
350 if (rpdd.isOKhasData()) {
352 if (nss==null || nss.contains(pdd.ns)) {
356 trans.error().log(pn,", derived from a Role, is invalid. Run Data Cleanup:",rpdd.errorString());
359 return Result.ok(rlpUser);
362 public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String type) {
363 if(type.indexOf('@') >= 0) {
364 int colon = type.indexOf(':');
366 return permDAO.readByType(trans, type.substring(0, colon),type.substring(colon+1));
368 return Result.err(Result.ERR_BadData, "%s is malformed",type);
371 Result<NsSplit> nss = deriveNsSplit(trans, type);
373 return Result.err(nss);
375 return permDAO.readByType(trans, nss.value.ns, nss.value.name);
379 public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
380 if(type.indexOf('@') >= 0) {
381 int colon = type.indexOf(':');
383 return permDAO.read(trans, type.substring(0, colon),type.substring(colon+1), instance,action);
385 return Result.err(Result.ERR_BadData, "%s is malformed",type);
388 Result<NsSplit> nss = deriveNsSplit(trans, type);
390 return Result.err(nss);
393 return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
397 public Result<List<PermDAO.Data>> getPermsByRole(AuthzTrans trans, String role, boolean lookup) {
398 Result<NsSplit> nss = deriveNsSplit(trans, role);
400 return Result.err(nss);
403 Result<List<RoleDAO.Data>> rlrd = roleDAO.read(trans, nss.value.ns,
405 if (rlrd.notOKorIsEmpty()) {
406 return Result.err(rlrd);
408 // Using Set to avoid duplicates
409 Set<String> permNames = new HashSet<>();
410 if (rlrd.isOKhasData()) {
411 for (RoleDAO.Data drr : rlrd.value) {
412 permNames.addAll(drr.perms(false));
416 // Note: It should be ok for a Valid user to have no permissions -
417 // Jonathan 8/12/2013
418 List<PermDAO.Data> perms = new ArrayList<>();
419 for (String perm : permNames) {
420 Result<PermDAO.Data> pr = PermDAO.Data.decode(trans, this, perm);
422 return Result.err(pr);
426 Result<List<PermDAO.Data>> rlpd = permDAO.read(trans, pr.value);
427 if (rlpd.isOKhasData()) {
428 for (PermDAO.Data pData : rlpd.value) {
437 return Result.ok(perms);
440 public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans, String role) {
441 if(role.startsWith(trans.user()) ) {
442 if(role.endsWith(":user")) {
443 return roleDAO.read(trans,trans.user(), "user");
445 return Result.err(Result.ERR_BadData,"%s is a badly formatted role",role);
448 Result<NsSplit> nss = deriveNsSplit(trans, role);
450 return Result.err(nss);
452 String r = nss.value.name;
453 if (r.endsWith(".*")) { // do children Search
454 return roleDAO.readChildren(trans, nss.value.ns,
455 r.substring(0, r.length() - 2));
456 } else if (ASTERIX.equals(r)) {
457 return roleDAO.readChildren(trans, nss.value.ns, ASTERIX);
459 return roleDAO.read(trans, nss.value.ns, r);
466 * Given a Child Namespace, figure out what the best Namespace parent is.
468 * For instance, if in the NS table, the parent "org.osaaf" exists, but not
469 * "org.osaaf.child" or "org.osaaf.a.b.c", then passing in either
470 * "org.osaaf.child" or "org.osaaf.a.b.c" will return "org.osaaf"
472 * Uses recursive search on Cached DAO data
478 public Result<NsDAO.Data> deriveNs(AuthzTrans trans, String child) {
479 Result<List<NsDAO.Data>> r = nsDAO.read(trans, child);
481 if (r.isOKhasData()) {
482 return Result.ok(r.value.get(0));
484 int dot = child.lastIndexOf('.');
486 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", child);
488 return deriveNs(trans, child.substring(0, dot));
493 public Result<NsDAO.Data> deriveFirstNsForType(AuthzTrans trans, String str, NsType type) {
496 for (String lookup = str;!".".equals(lookup) && lookup!=null;) {
497 Result<List<NsDAO.Data>> rld = nsDAO.read(trans, lookup);
498 if (rld.isOKhasData()) {
499 nsd=rld.value.get(0);
501 if (type.type == nsd.type) {
502 return Result.ok(nsd);
504 int dot = str.lastIndexOf('.');
507 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
509 return deriveFirstNsForType(trans, str.substring(0, dot),type);
513 int dot = str.lastIndexOf('.');
516 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
518 return deriveFirstNsForType(trans, str.substring(0, dot),type);
522 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
525 public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
526 Result<NsDAO.Data> ndd = deriveNs(trans, child);
528 NsSplit nss = new NsSplit(ndd.value, child);
530 return Result.ok(nss);
532 return Result.err(Status.ERR_NsNotFound,
533 "Cannot split [%s] into valid namespace elements",
537 return Result.err(ndd);
541 * Translate an ID into it's domain
543 * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
548 public static String domain2ns(String id) {
549 int at = id.indexOf('@');
551 String[] domain = id.substring(at + 1).split("\\.");
552 StringBuilder ns = new StringBuilder(id.length());
553 boolean first = true;
554 for (int i = domain.length - 1; i >= 0; --i) {
560 ns.append(domain[i]);
562 return ns.toString();
570 * Validate Namespace of ID@Domain
572 * Namespace is reverse order of Domain.
578 public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
579 // Take domain, reverse order, and check on NS
581 if (id.indexOf('@')<0) { // it's already an ns, not an ID
586 if (ns.length() > 0) {
587 if (!trans.org().getDomain().equals(ns)) {
588 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
589 if (rlnsd.isOKhasData()) {
590 return Result.ok(rlnsd.value.get(0));
594 return Result.err(Status.ERR_NsNotFound,
595 "A Namespace is not available for %s", id);
598 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
599 // <ns>.access|:role:<role name>|<read|write>
600 String ns = ndd.name;
601 boolean isRoot = ns.startsWith(Define.ROOT_NS());
604 if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
605 return Result.ok(ndd);
610 if ((last = ns.lastIndexOf('.')) >= 0) {
611 ns = ns.substring(0, last);
615 // SAFETY - Do not allow these when NS is Root
617 // com.att.aaf.ns|:<client ns>:ns|<access>
618 // AAF-724 - Make consistent response for May User", and not take the
619 // last check... too confusing.
620 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
623 } else if (rv.status==Result.ERR_Backend) {
624 return Result.err(rv);
627 return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
628 user, access.name(), ndd.name);
632 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
633 if(trans.user().equals(rdd.ns)) {
634 return Result.ok((NsDAO.Data)null);
636 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
638 return mayUser(trans, user, rnsd.value, rdd, access);
643 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
644 // 1) For "read", Is User in the Role is enough
645 if(Access.read.equals(access)) {
646 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
647 if (rurd.isOKhasData()) {
648 return Result.ok(ndd);
652 String roleInst = ":role:" + rdd.name;
653 // <ns>.access|:role:<role name>|<read|write>
655 boolean isRoot = ns.startsWith(Define.ROOT_NS());
658 if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
659 return Result.ok(ndd);
664 if ((last = ns.lastIndexOf('.')) >= 0) {
665 ns = ns.substring(0, last);
669 // SAFETY - Do not allow these when NS is Root
671 // Check if Access by Global Role perm
672 // com.att.aaf.ns|:<client ns>:role:name|<access>
673 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
674 + rdd.ns + roleInst, access.name());
677 } else if (rnsd.status==Result.ERR_Backend) {
678 return Result.err(rnsd);
681 // Check if Access to Whole NS
682 // AAF-724 - Make consistent response for May User", and not take the
683 // last check... too confusing.
684 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd,
685 ":" + rdd.ns + ":ns", access.name());
688 } else if (rnsd.status==Result.ERR_Backend) {
689 return Result.err(rnsd);
692 return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
693 user, access.name(), rdd.fullName());
696 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
697 if(pdd.ns.indexOf('@')>-1) {
698 if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
699 NsDAO.Data ndd = new NsDAO.Data();
701 ndd.type = NsDAO.USER;
703 return Result.ok(ndd);
705 return Result.err(Result.ERR_Security,"Only a User may modify User");
708 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
710 return mayUser(trans, user, rnsd.value, pdd, access);
715 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
716 // Most common occurrence... if granted Permission
717 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
718 return Result.ok(ndd);
721 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
722 // <ns>.access|:role:<role name>|<read|write>
723 String ns = ndd.name;
724 boolean isRoot = ns.startsWith(Define.ROOT_NS());
727 if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
728 return Result.ok(ndd);
733 if ((last = ns.lastIndexOf('.')) >= 0) {
734 ns = ns.substring(0, last);
738 // SAFETY - Do not allow these when NS is Root
740 // Check if Access by NS perm
741 // com.att.aaf.ns|:<client ns>:role:name|<access>
742 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
745 } else if (rnsd.status==Result.ERR_Backend) {
746 return Result.err(rnsd);
749 // Check if Access to Whole NS
750 // AAF-724 - Make consistent response for May User", and not take the
751 // last check... too confusing.
752 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
757 return Result.err(Status.ERR_Denied,
758 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
759 pdd.fullType(), pdd.instance, pdd.action);
762 public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
764 Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
765 if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":" + rnsd.value.name + ":ns", access.name()).isOK()) {
768 boolean isUser = trans.user().equals(dd.user);
769 boolean isDelegate = dd.delegate != null
770 && (dd.user.equals(dd.delegate) || trans.user().equals(
772 Organization org = trans.org();
775 if (org.getIdentity(trans, dd.user) == null) {
776 return Result.err(Status.ERR_UserNotFound,
777 "[%s] is not a user in the company database.",
780 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
781 return Result.err(Status.ERR_UserNotFound,
782 "[%s] is not a user in the company database.",
785 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
786 return Result.err(Status.ERR_BadData,
787 "[%s] cannot be a delegate for self", dd.user);
789 if (!isUser && !isGranted(trans, trans.user(), ROOT_NS,DELG,
790 org.getDomain(), Question.CREATE)) {
791 return Result.err(Status.ERR_Denied,
792 "[%s] may not create a delegate for [%s]",
793 trans.user(), dd.user);
798 if (!isUser && !isDelegate &&
799 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
800 return Result.err(Status.ERR_Denied,
801 "[%s] may not %s delegates for [%s]", trans.user(),
802 access.name(), dd.user);
806 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
808 } catch (Exception e) {
809 return Result.err(e);
815 * Check (recursively, if necessary), if able to do something based on NS
817 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
818 String ns = nsd.name;
820 // If an ADMIN of the Namespace, then allow
822 Result<List<UserRoleDAO.Data>> rurd;
823 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
824 return Result.ok(nsd);
825 } else if (rurd.status==Result.ERR_Backend) {
826 return Result.err(rurd);
829 // If Specially granted Global Permission
830 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
831 return Result.ok(nsd);
836 int dot = ns.length();
837 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
838 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
840 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
841 } else if (rnsd.status==Result.ERR_Backend) {
842 return Result.err(rnsd);
845 return Result.ok(nsd);
846 } else if (rnsd.status==Result.ERR_Backend) {
847 return Result.err(rnsd);
850 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
858 * Important function - Check internal Permission Schemes for Permission to
867 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
868 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
870 for (PermDAO.Data pd : perms.value) {
871 if (ns.equals(pd.ns)) {
872 if (type.equals(pd.type)) {
873 if (PermEval.evalInstance(pd.instance, instance)) {
874 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
885 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
886 Result<List<CredDAO.Data>> result;
887 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
889 result = credDAO.readIDBAth(trans, user);
894 Result<Date> rv = null;
896 if (result.isEmpty()) {
897 rv = Result.err(Status.ERR_UserNotFound, user);
898 if (willSpecialLog(trans,user)) {
899 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
902 Date now = new Date();
903 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
904 // 9/14/2019. Use TreeSet for sorting, and using only the LAST of a Tagged entry
905 Collection<CredDAO.Data> cddl;
906 if (result.value.size() > 1) {
907 Map<String,CredDAO.Data> mcdd = new TreeMap<>();
911 for (CredDAO.Data rcdd : result.value) {
912 if (rcdd.type==CredDAO.BASIC_AUTH || rcdd.type==CredDAO.BASIC_AUTH_SHA256) {
914 mcdd.put(Integer.toString(++pseudoTag),rcdd);
918 if(cdd==null || cdd.expires.before(rcdd.expires)) {
924 cddl = mcdd.values();
930 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
931 for (CredDAO.Data cdd : cddl) {
932 if (!cdd.id.equals(user)) {
933 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
935 if (cdd.expires.after(now)) {
936 byte[] dbcred = cdd.cred.array();
940 case CredDAO.BASIC_AUTH:
941 byte[] md5=Hash.hashMD5(cred);
942 if (Hash.compareTo(md5,dbcred)==0) {
943 checkLessThanDays(trans,cldays,now,cdd);
944 trans.setTag(cdd.tag);
945 return Result.ok(cdd.expires);
946 } else if (debug!=null) {
950 case CredDAO.BASIC_AUTH_SHA256:
951 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
952 bb.putInt(cdd.other);
954 byte[] hash = Hash.hashSHA256(bb.array());
956 if (Hash.compareTo(hash,dbcred)==0) {
957 checkLessThanDays(trans,cldays,now,cdd);
958 trans.setTag(cdd.tag);
959 return Result.ok(cdd.expires);
960 } else if (debug!=null) {
965 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
967 } catch (NoSuchAlgorithmException e) {
968 trans.error().log(e);
971 if (expired==null || expired.before(cdd.expires)) {
972 expired = cdd.expires;
973 trans.setTag(cdd.tag);
979 // Note: this is only returned if there are no good Credentials
980 rv = Result.err(Status.ERR_Security,
981 "Credentials expired %s",Chrono.utcStamp(expired));
983 if (debug==null && alwaysSpecial) {
984 debug = new StringBuilder();
987 debug.append(trans.env().encryptor().encrypt(new String(cred)));
988 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
993 return Result.err(result);
995 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
999 private void load(StringBuilder debug, Data cdd) {
1000 debug.append("\nDB Entry: user=");
1001 debug.append(cdd.id);
1002 debug.append(",type=");
1003 debug.append(cdd.type);
1004 debug.append(",expires=");
1005 debug.append(Chrono.dateTime(cdd.expires));
1006 debug.append(",tag=");
1007 debug.append(cdd.tag);
1012 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1013 long close = now.getTime() + (days * 86400000);
1014 long cexp=cdd.expires.getTime();
1016 int daysLeft = days-(int)((close-cexp)/86400000);
1017 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1018 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
1019 daysLeft,daysLeft==1?"":"s");
1024 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1025 if (cred.type==CredDAO.RAW) {
1026 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1028 cred.type = CredDAO.BASIC_AUTH_SHA256;
1029 cred.other = random.nextInt();
1030 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1031 bb.putInt(cred.other);
1033 byte[] hash = Hash.hashSHA256(bb.array());
1034 cred.cred = ByteBuffer.wrap(hash);
1035 return Result.ok(cred);
1036 } catch (NoSuchAlgorithmException e) {
1037 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1042 } else if (cred.type==CredDAO.FQI) {
1044 return Result.ok(cred);
1046 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1049 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1051 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1054 case CredDAO.BASIC_AUTH_SHA256:
1055 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1056 bb.putInt(orig.other);
1058 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1059 case CredDAO.BASIC_AUTH:
1060 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1063 rv = Result.ok(false);
1065 } catch (NoSuchAlgorithmException e) {
1066 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1073 public static final String APPROVED = "APPROVE";
1074 public static final String REJECT = "REJECT";
1075 public static final String PENDING = "PENDING";
1077 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1078 List<ApprovalDAO.Data> approvals) {
1079 // get the approval policy for the organization
1081 // get the list of approvals with an accept status
1083 // validate the approvals against the policy
1085 // for now check if all approvals are received and return
1086 // SUCCESS/FAILURE/SKIP
1087 boolean bReject = false;
1088 boolean bPending = false;
1090 for (ApprovalDAO.Data approval : approvals) {
1091 if (approval.status.equals(REJECT)) {
1093 } else if (approval.status.equals(PENDING)) {
1098 return Result.err(Status.ERR_Policy,
1099 "Approval Polocy not conformed");
1102 return Result.err(Status.ERR_ActionNotCompleted,
1103 "Required Approvals not received");
1109 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1111 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1112 boolean all = "all".equals(cname);
1113 Result<Void> rv = null;
1115 if (all || NsDAO.TABLE.equals(cname)) {
1116 int[] seg = series(NsDAO.CACHE_SEG);
1117 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1118 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1120 if (all || PermDAO.TABLE.equals(cname)) {
1121 int[] seg = series(PermDAO.CACHE_SEG);
1122 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1123 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1125 if (all || RoleDAO.TABLE.equals(cname)) {
1126 int[] seg = series(RoleDAO.CACHE_SEG);
1127 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1128 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1130 if (all || UserRoleDAO.TABLE.equals(cname)) {
1131 int[] seg = series(UserRoleDAO.CACHE_SEG);
1132 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1133 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1135 if (all || CredDAO.TABLE.equals(cname)) {
1136 int[] seg = series(CredDAO.CACHE_SEG);
1137 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1138 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1140 if (all || CertDAO.TABLE.equals(cname)) {
1141 int[] seg = series(CertDAO.CACHE_SEG);
1142 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1143 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1147 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1152 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1154 if (NsDAO.TABLE.equals(cname)) {
1155 rv = nsDAO.invalidate(segment);
1156 } else if (PermDAO.TABLE.equals(cname)) {
1157 rv = permDAO.invalidate(segment);
1158 } else if (RoleDAO.TABLE.equals(cname)) {
1159 rv = roleDAO.invalidate(segment);
1160 } else if (UserRoleDAO.TABLE.equals(cname)) {
1161 rv = userRoleDAO.invalidate(segment);
1162 } else if (CredDAO.TABLE.equals(cname)) {
1163 rv = credDAO.invalidate(segment);
1164 } else if (CertDAO.TABLE.equals(cname)) {
1165 rv = certDAO.invalidate(segment);
1167 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1172 private int[] series(int max) {
1173 int[] series = new int[max];
1174 for (int i = 0; i < max; ++i)
1179 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1180 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1181 if (userDelegatedFor==null) {
1182 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1183 rldd.put(user, userDelegatedFor);
1185 if (userDelegatedFor.isOKhasData()) {
1186 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1187 if (curr.user.equals(approver) && curr.delegate.equals(user)
1188 && curr.expires.after(new Date())) {
1196 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1197 Boolean b = trans.get(specialLogSlot, null);
1198 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1199 if (specialLog==null) {
1202 b = specialLog.contains(user);
1203 trans.put(specialLogSlot, b);
1209 public static void logEncryptTrace(AuthzTrans trans, String data) {
1211 trans.put(transIDSlot, ti=nextTraceID());
1212 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1215 private synchronized static long nextTraceID() {
1219 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1220 if (specialLog == null) {
1221 specialLog = new HashSet<>();
1223 boolean rc = specialLog.add(id);
1225 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1230 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1231 if (specialLog==null) {
1234 boolean rv = specialLog.remove(id);
1235 if (specialLog.isEmpty()) {
1239 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1246 * Which Types can be moved
1250 public boolean canMove(NsType nsType) {
1265 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1266 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1267 if (rur.isOKhasData()) {
1268 Date now = new Date();
1269 for (UserRoleDAO.Data urdd : rur.value){
1270 if (urdd.expires.after(now)) {
1278 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1279 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1280 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1281 Date now = new Date();
1282 if (urdd.expires.after(now)) {
1289 public int countOwner(AuthzTrans trans, String ns) {
1290 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1291 Date now = new Date();
1293 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1294 if (urdd.expires.after(now)) {
1302 * Return a Unique String, (same string, if it is already unique), with only
1303 * lowercase letters, digits and the '.' character.
1307 * @throws IOException
1309 public static String toUnique(String name) throws IOException {
1310 byte[] from = name.getBytes();
1311 StringBuilder sb = new StringBuilder();
1313 for (int i=0;i<from.length;++i) {
1314 f=(byte)(from[i]); // printables;
1315 sb.append((char)((f>>4)+0x61));
1316 sb.append((char)((f&0x0F)+0x61));
1318 return sb.toString();
1321 public static String fromUnique(String name) throws IOException {
1322 byte[] from = name.getBytes();
1323 StringBuilder sb = new StringBuilder();
1325 for (int i=0;i<from.length;++i) {
1326 c = (char)((from[i]-0x61)<<4);
1327 c |= (from[++i]-0x61);
1330 return sb.toString();