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.Collections;
30 import java.util.Date;
31 import java.util.HashSet;
32 import java.util.List;
34 import java.util.Random;
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.cass.ApprovalDAO;
49 import org.onap.aaf.auth.dao.cass.CacheInfoDAO;
50 import org.onap.aaf.auth.dao.cass.CertDAO;
51 import org.onap.aaf.auth.dao.cass.CredDAO;
52 import org.onap.aaf.auth.dao.cass.CredDAO.Data;
53 import org.onap.aaf.auth.dao.cass.DelegateDAO;
54 import org.onap.aaf.auth.dao.cass.FutureDAO;
55 import org.onap.aaf.auth.dao.cass.HistoryDAO;
56 import org.onap.aaf.auth.dao.cass.LocateDAO;
57 import org.onap.aaf.auth.dao.cass.NsDAO;
58 import org.onap.aaf.auth.dao.cass.NsSplit;
59 import org.onap.aaf.auth.dao.cass.NsType;
60 import org.onap.aaf.auth.dao.cass.PermDAO;
61 import org.onap.aaf.auth.dao.cass.RoleDAO;
62 import org.onap.aaf.auth.dao.cass.Status;
63 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
64 import org.onap.aaf.auth.env.AuthzEnv;
65 import org.onap.aaf.auth.env.AuthzTrans;
66 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
67 import org.onap.aaf.auth.env.AuthzTransFilter;
68 import org.onap.aaf.auth.layer.Result;
69 import org.onap.aaf.auth.org.Organization;
70 import org.onap.aaf.cadi.Hash;
71 import org.onap.aaf.cadi.aaf.PermEval;
72 import org.onap.aaf.cadi.config.Config;
73 import org.onap.aaf.misc.env.APIException;
74 import org.onap.aaf.misc.env.Env;
75 import org.onap.aaf.misc.env.Slot;
76 import org.onap.aaf.misc.env.TimeTaken;
77 import org.onap.aaf.misc.env.util.Chrono;
79 import com.datastax.driver.core.Cluster;
84 * A Data Access Combination Object which asks Security and other Questions
89 public class Question {
91 // DON'T CHANGE FROM lower Case!!!
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";
102 static final String ASTERIX = "*";
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();
112 public static final String ROLE = Type.role.name();
113 public static final String PERM = Type.perm.name();
114 public static final String NS = Type.ns.name();
115 public static final String CRED = Type.cred.name();
116 private static final String DELG = "delg";
117 public static final String ROOT_NS = Define.isInitialized() ? Define.ROOT_NS() : "undefined";
118 public static final String ATTRIB = "attrib";
121 public static final int MAX_SCOPE = 10;
122 public static final int APP_SCOPE = 3;
123 public static final int COMPANY_SCOPE = 2;
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;
133 private final HistoryDAO historyDAO;
134 public HistoryDAO historyDAO() {
138 private final CachedNSDAO nsDAO;
139 public CachedNSDAO nsDAO() {
143 private final CachedRoleDAO roleDAO;
144 public CachedRoleDAO roleDAO() {
148 private final CachedPermDAO permDAO;
149 public CachedPermDAO permDAO() {
153 private final CachedUserRoleDAO userRoleDAO;
154 public CachedUserRoleDAO userRoleDAO() {
158 private final CachedCredDAO credDAO;
159 public CachedCredDAO credDAO() {
163 private final CachedCertDAO certDAO;
164 public CachedCertDAO certDAO() {
168 private final DelegateDAO delegateDAO;
169 public DelegateDAO delegateDAO() {
173 private final FutureDAO futureDAO;
174 public FutureDAO futureDAO() {
178 private final ApprovalDAO approvalDAO;
179 public ApprovalDAO approvalDAO() {
183 public final LocateDAO locateDAO;
184 public LocateDAO locateDAO() {
188 private final CacheInfoDAO cacheInfoDAO;
189 private final int cldays;
190 private final boolean alwaysSpecial;
192 public Question(AuthzTrans trans, Cluster cluster, String keyspace) throws APIException, IOException {
193 PERMS = trans.slot("USER_PERMS");
194 trans.init().log("Instantiating DAOs");
195 long expiresIn = Long.parseLong(trans.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF));
196 historyDAO = new HistoryDAO(trans, cluster, keyspace);
198 // Deal with Cached Entries
199 cacheInfoDAO = new CacheInfoDAO(trans, historyDAO);
201 nsDAO = new CachedNSDAO(new NsDAO(trans, historyDAO, cacheInfoDAO),cacheInfoDAO, expiresIn);
202 permDAO = new CachedPermDAO(new PermDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
203 roleDAO = new CachedRoleDAO(new RoleDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
204 userRoleDAO = new CachedUserRoleDAO(new UserRoleDAO(trans, historyDAO,cacheInfoDAO), cacheInfoDAO, expiresIn);
205 credDAO = new CachedCredDAO(new CredDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
206 certDAO = new CachedCertDAO(new CertDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
208 locateDAO = new LocateDAO(trans,historyDAO);
209 futureDAO = new FutureDAO(trans, historyDAO);
210 delegateDAO = new DelegateDAO(trans, historyDAO);
211 approvalDAO = new ApprovalDAO(trans, historyDAO);
213 if (specialLogSlot==null) {
214 specialLogSlot = trans.slot(AuthzTransFilter.SPECIAL_LOG_SLOT);
217 if (transIDSlot==null) {
218 transIDSlot = trans.slot(AuthzTransFilter.TRANS_ID_SLOT);
221 AbsCassDAO.primePSIs(trans);
223 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
225 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
229 * Note: This Constructor created for JUNIT Purposes. Do not use otherwise.
231 public Question(AuthzTrans trans, HistoryDAO historyDAO, CacheInfoDAO cacheInfoDAO,
232 CachedNSDAO nsDAO, CachedPermDAO permDAO, CachedRoleDAO roleDAO,
233 CachedUserRoleDAO userRoleDAO, CachedCredDAO credDAO, CachedCertDAO certDAO,
234 LocateDAO locateDAO,FutureDAO futureDAO, DelegateDAO delegateDAO,
235 ApprovalDAO approvalDAO ) {
236 this.historyDAO = historyDAO;
237 this.cacheInfoDAO = cacheInfoDAO;
239 this.permDAO = permDAO;
240 this.roleDAO = roleDAO;
241 this.userRoleDAO = userRoleDAO;
242 this.credDAO = credDAO;
243 this.certDAO = certDAO;
244 this.locateDAO = locateDAO;
245 this.futureDAO = futureDAO;
246 this.delegateDAO = delegateDAO;
247 this.approvalDAO = approvalDAO;
249 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
250 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
253 public void startTimers(AuthzEnv env) {
254 // Only want to aggressively cleanse User related Caches... The others,
255 // just normal refresh
256 CachedDAO.startCleansing(env, credDAO, userRoleDAO);
257 CachedDAO.startRefresh(env, cacheInfoDAO);
260 public void close(AuthzTrans trans) {
261 historyDAO.close(trans);
262 cacheInfoDAO.close(trans);
264 permDAO.close(trans);
265 roleDAO.close(trans);
266 userRoleDAO.close(trans);
267 credDAO.close(trans);
268 certDAO.close(trans);
269 delegateDAO.close(trans);
270 futureDAO.close(trans);
271 approvalDAO.close(trans);
274 public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type, String instance, String action) {
275 if(type.indexOf('@') >= 0) {
276 int colon = type.indexOf(':');
278 PermDAO.Data pdd = new PermDAO.Data();
279 pdd.ns = type.substring(0, colon);
280 pdd.type = type.substring(colon+1);
281 pdd.instance = instance;
284 return Result.ok(pdd);
286 return Result.err(Result.ERR_BadData,"Could not extract ns and type from " + type);
289 Result<NsDAO.Data> rnd = deriveNs(trans, type);
291 return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
294 return Result.err(rnd);
302 * Because this call is frequently called internally, AND because we already
303 * look for it in the initial Call, we cache within the Transaction
309 public Result<List<PermDAO.Data>> getPermsByUser(AuthzTrans trans, String user, boolean lookup) {
310 return PermLookup.get(trans, this, user).getPerms(lookup);
313 public Result<List<PermDAO.Data>> getPermsByUserFromRolesFilter(AuthzTrans trans, String user, String forUser) {
314 PermLookup plUser = PermLookup.get(trans, this, user);
315 Result<Set<String>> plPermNames = plUser.getPermNames();
316 if (plPermNames.notOK()) {
317 return Result.err(plPermNames);
321 if (forUser.equals(user)) {
324 // Setup a TreeSet to check on Namespaces to
325 nss = new TreeSet<>();
326 PermLookup fUser = PermLookup.get(trans, this, forUser);
327 Result<Set<String>> forUpn = fUser.getPermNames();
328 if (forUpn.notOK()) {
329 return Result.err(forUpn);
332 for (String pn : forUpn.value) {
333 Result<String[]> decoded = PermDAO.Data.decodeToArray(trans, this, pn);
334 if (decoded.isOKhasData()) {
335 nss.add(decoded.value[0]);
337 trans.error().log(pn,", derived from a Role, is invalid:",decoded.errorString());
342 List<PermDAO.Data> rlpUser = new ArrayList<>();
343 Result<PermDAO.Data> rpdd;
345 for (String pn : plPermNames.value) {
346 rpdd = PermDAO.Data.decode(trans, this, pn);
347 if (rpdd.isOKhasData()) {
349 if (nss==null || nss.contains(pdd.ns)) {
353 trans.error().log(pn,", derived from a Role, is invalid. Run Data Cleanup:",rpdd.errorString());
356 return Result.ok(rlpUser);
359 public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String type) {
360 if(type.indexOf('@') >= 0) {
361 int colon = type.indexOf(':');
363 return permDAO.readByType(trans, type.substring(0, colon),type.substring(colon+1));
365 return Result.err(Result.ERR_BadData, "%s is malformed",type);
368 Result<NsSplit> nss = deriveNsSplit(trans, type);
370 return Result.err(nss);
372 return permDAO.readByType(trans, nss.value.ns, nss.value.name);
376 public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
377 if(type.indexOf('@') >= 0) {
378 int colon = type.indexOf(':');
380 return permDAO.read(trans, type.substring(0, colon),type.substring(colon+1), instance,action);
382 return Result.err(Result.ERR_BadData, "%s is malformed",type);
385 Result<NsSplit> nss = deriveNsSplit(trans, type);
387 return Result.err(nss);
390 return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
394 public Result<List<PermDAO.Data>> getPermsByRole(AuthzTrans trans, String role, boolean lookup) {
395 Result<NsSplit> nss = deriveNsSplit(trans, role);
397 return Result.err(nss);
400 Result<List<RoleDAO.Data>> rlrd = roleDAO.read(trans, nss.value.ns,
402 if (rlrd.notOKorIsEmpty()) {
403 return Result.err(rlrd);
405 // Using Set to avoid duplicates
406 Set<String> permNames = new HashSet<>();
407 if (rlrd.isOKhasData()) {
408 for (RoleDAO.Data drr : rlrd.value) {
409 permNames.addAll(drr.perms(false));
413 // Note: It should be ok for a Valid user to have no permissions -
414 // Jonathan 8/12/2013
415 List<PermDAO.Data> perms = new ArrayList<>();
416 for (String perm : permNames) {
417 Result<PermDAO.Data> pr = PermDAO.Data.decode(trans, this, perm);
419 return Result.err(pr);
423 Result<List<PermDAO.Data>> rlpd = permDAO.read(trans, pr.value);
424 if (rlpd.isOKhasData()) {
425 for (PermDAO.Data pData : rlpd.value) {
434 return Result.ok(perms);
437 public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans, String role) {
438 if(role.startsWith(trans.user()) ) {
439 if(role.endsWith(":user")) {
440 return roleDAO.read(trans,trans.user(), "user");
442 return Result.err(Result.ERR_BadData,"%s is a badly formatted role",role);
445 Result<NsSplit> nss = deriveNsSplit(trans, role);
447 return Result.err(nss);
449 String r = nss.value.name;
450 if (r.endsWith(".*")) { // do children Search
451 return roleDAO.readChildren(trans, nss.value.ns,
452 r.substring(0, r.length() - 2));
453 } else if (ASTERIX.equals(r)) {
454 return roleDAO.readChildren(trans, nss.value.ns, ASTERIX);
456 return roleDAO.read(trans, nss.value.ns, r);
463 * Given a Child Namespace, figure out what the best Namespace parent is.
465 * For instance, if in the NS table, the parent "org.osaaf" exists, but not
466 * "org.osaaf.child" or "org.osaaf.a.b.c", then passing in either
467 * "org.osaaf.child" or "org.osaaf.a.b.c" will return "org.osaaf"
469 * Uses recursive search on Cached DAO data
475 public Result<NsDAO.Data> deriveNs(AuthzTrans trans, String child) {
476 Result<List<NsDAO.Data>> r = nsDAO.read(trans, child);
478 if (r.isOKhasData()) {
479 return Result.ok(r.value.get(0));
481 int dot = child.lastIndexOf('.');
483 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", child);
485 return deriveNs(trans, child.substring(0, dot));
490 public Result<NsDAO.Data> deriveFirstNsForType(AuthzTrans trans, String str, NsType type) {
493 for (String lookup = str;!".".equals(lookup) && lookup!=null;) {
494 Result<List<NsDAO.Data>> rld = nsDAO.read(trans, lookup);
495 if (rld.isOKhasData()) {
496 nsd=rld.value.get(0);
498 if (type.type == nsd.type) {
499 return Result.ok(nsd);
501 int dot = str.lastIndexOf('.');
504 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
506 return deriveFirstNsForType(trans, str.substring(0, dot),type);
510 int dot = str.lastIndexOf('.');
513 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
515 return deriveFirstNsForType(trans, str.substring(0, dot),type);
519 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
522 public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
523 Result<NsDAO.Data> ndd = deriveNs(trans, child);
525 NsSplit nss = new NsSplit(ndd.value, child);
527 return Result.ok(nss);
529 return Result.err(Status.ERR_NsNotFound,
530 "Cannot split [%s] into valid namespace elements",
534 return Result.err(ndd);
538 * Translate an ID into it's domain
540 * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
545 public static String domain2ns(String id) {
546 int at = id.indexOf('@');
548 String[] domain = id.substring(at + 1).split("\\.");
549 StringBuilder ns = new StringBuilder(id.length());
550 boolean first = true;
551 for (int i = domain.length - 1; i >= 0; --i) {
557 ns.append(domain[i]);
559 return ns.toString();
567 * Validate Namespace of ID@Domain
569 * Namespace is reverse order of Domain.
575 public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
576 // Take domain, reverse order, and check on NS
578 if (id.indexOf('@')<0) { // it's already an ns, not an ID
583 if (ns.length() > 0) {
584 if (!trans.org().getDomain().equals(ns)) {
585 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
586 if (rlnsd.isOKhasData()) {
587 return Result.ok(rlnsd.value.get(0));
591 return Result.err(Status.ERR_NsNotFound,
592 "A Namespace is not available for %s", id);
595 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
596 // <ns>.access|:role:<role name>|<read|write>
597 String ns = ndd.name;
600 if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
601 return Result.ok(ndd);
603 if ((last = ns.lastIndexOf('.')) >= 0) {
604 ns = ns.substring(0, last);
607 // com.att.aaf.ns|:<client ns>:ns|<access>
608 // AAF-724 - Make consistent response for May User", and not take the
609 // last check... too confusing.
610 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
613 } else if (rv.status==Result.ERR_Backend) {
614 return Result.err(rv);
616 return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
617 user, access.name(), ndd.name);
621 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
622 if(trans.user().equals(rdd.ns)) {
623 return Result.ok((NsDAO.Data)null);
625 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
627 return mayUser(trans, user, rnsd.value, rdd, access);
632 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
633 // 1) Is User in the Role?
634 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
635 if (rurd.isOKhasData()) {
636 return Result.ok(ndd);
639 String roleInst = ":role:" + rdd.name;
640 // <ns>.access|:role:<role name>|<read|write>
644 if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
645 return Result.ok(ndd);
647 if ((last = ns.lastIndexOf('.')) >= 0) {
648 ns = ns.substring(0, last);
652 // Check if Access by Global Role perm
653 // com.att.aaf.ns|:<client ns>:role:name|<access>
654 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
655 + rdd.ns + roleInst, access.name());
658 } else if (rnsd.status==Result.ERR_Backend) {
659 return Result.err(rnsd);
662 // Check if Access to Whole NS
663 // AAF-724 - Make consistent response for May User", and not take the
664 // last check... too confusing.
665 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd,
666 ":" + rdd.ns + ":ns", access.name());
669 } else if (rnsd.status==Result.ERR_Backend) {
670 return Result.err(rnsd);
672 return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
673 user, access.name(), rdd.fullName());
678 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
679 if(pdd.ns.indexOf('@')>-1) {
680 if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
681 NsDAO.Data ndd = new NsDAO.Data();
683 ndd.type = NsDAO.USER;
685 return Result.ok(ndd);
687 return Result.err(Result.ERR_Security,"Only a User may modify User");
690 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
692 return mayUser(trans, user, rnsd.value, pdd, access);
697 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
698 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
699 return Result.ok(ndd);
701 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
702 // <ns>.access|:role:<role name>|<read|write>
703 String ns = ndd.name;
706 if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
707 return Result.ok(ndd);
709 if ((last = ns.lastIndexOf('.')) >= 0) {
710 ns = ns.substring(0, last);
714 // Check if Access by NS perm
715 // com.att.aaf.ns|:<client ns>:role:name|<access>
716 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
719 } else if (rnsd.status==Result.ERR_Backend) {
720 return Result.err(rnsd);
723 // Check if Access to Whole NS
724 // AAF-724 - Make consistent response for May User", and not take the
725 // last check... too confusing.
726 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
730 return Result.err(Status.ERR_Denied,
731 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
732 pdd.fullType(), pdd.instance, pdd.action);
737 public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
739 Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
740 if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":" + rnsd.value.name + ":ns", access.name()).isOK()) {
743 boolean isUser = trans.user().equals(dd.user);
744 boolean isDelegate = dd.delegate != null
745 && (dd.user.equals(dd.delegate) || trans.user().equals(
747 Organization org = trans.org();
750 if (org.getIdentity(trans, dd.user) == null) {
751 return Result.err(Status.ERR_UserNotFound,
752 "[%s] is not a user in the company database.",
755 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
756 return Result.err(Status.ERR_UserNotFound,
757 "[%s] is not a user in the company database.",
760 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
761 return Result.err(Status.ERR_BadData,
762 "[%s] cannot be a delegate for self", dd.user);
764 if (!isUser && !isGranted(trans, trans.user(), ROOT_NS,DELG,
765 org.getDomain(), Question.CREATE)) {
766 return Result.err(Status.ERR_Denied,
767 "[%s] may not create a delegate for [%s]",
768 trans.user(), dd.user);
773 if (!isUser && !isDelegate &&
774 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
775 return Result.err(Status.ERR_Denied,
776 "[%s] may not %s delegates for [%s]", trans.user(),
777 access.name(), dd.user);
781 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
783 } catch (Exception e) {
784 return Result.err(e);
790 * Check (recursively, if necessary), if able to do something based on NS
792 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
793 String ns = nsd.name;
795 // If an ADMIN of the Namespace, then allow
797 Result<List<UserRoleDAO.Data>> rurd;
798 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
799 return Result.ok(nsd);
800 } else if (rurd.status==Result.ERR_Backend) {
801 return Result.err(rurd);
804 // If Specially granted Global Permission
805 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
806 return Result.ok(nsd);
811 int dot = ns.length();
812 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
813 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
815 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
816 } else if (rnsd.status==Result.ERR_Backend) {
817 return Result.err(rnsd);
820 return Result.ok(nsd);
821 } else if (rnsd.status==Result.ERR_Backend) {
822 return Result.err(rnsd);
825 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
833 * Important function - Check internal Permission Schemes for Permission to
842 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
843 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
845 for (PermDAO.Data pd : perms.value) {
846 if (ns.equals(pd.ns)) {
847 if (type.equals(pd.type)) {
848 if (PermEval.evalInstance(pd.instance, instance)) {
849 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
860 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
861 Result<List<CredDAO.Data>> result;
862 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
864 result = credDAO.readID(trans, user);
869 Result<Date> rv = null;
871 if (result.isEmpty()) {
872 rv = Result.err(Status.ERR_UserNotFound, user);
873 if (willSpecialLog(trans,user)) {
874 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
877 Date now = new Date();
878 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
879 List<CredDAO.Data> cddl;
880 if (result.value.size() > 1) {
881 cddl = new ArrayList<>(result.value.size());
882 for (CredDAO.Data old : result.value) {
883 if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
888 Collections.sort(cddl, (a, b) -> b.expires.compareTo(a.expires));
895 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
896 for (CredDAO.Data cdd : cddl) {
897 if (!cdd.id.equals(user)) {
898 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
900 if (cdd.expires.after(now)) {
901 byte[] dbcred = cdd.cred.array();
905 case CredDAO.BASIC_AUTH:
906 byte[] md5=Hash.hashMD5(cred);
907 if (Hash.compareTo(md5,dbcred)==0) {
908 checkLessThanDays(trans,cldays,now,cdd);
909 trans.setTag(cdd.tag);
910 return Result.ok(cdd.expires);
911 } else if (debug!=null) {
915 case CredDAO.BASIC_AUTH_SHA256:
916 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
917 bb.putInt(cdd.other);
919 byte[] hash = Hash.hashSHA256(bb.array());
921 if (Hash.compareTo(hash,dbcred)==0) {
922 checkLessThanDays(trans,cldays,now,cdd);
923 trans.setTag(cdd.tag);
924 return Result.ok(cdd.expires);
925 } else if (debug!=null) {
930 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
932 } catch (NoSuchAlgorithmException e) {
933 trans.error().log(e);
936 if (expired==null || expired.before(cdd.expires)) {
937 expired = cdd.expires;
938 trans.setTag(cdd.tag);
944 // Note: this is only returned if there are no good Credentials
945 rv = Result.err(Status.ERR_Security,
946 "Credentials expired %s",Chrono.utcStamp(expired));
948 if (debug==null && alwaysSpecial) {
949 debug = new StringBuilder();
952 debug.append(trans.env().encryptor().encrypt(new String(cred)));
953 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
958 return Result.err(result);
960 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
964 private void load(StringBuilder debug, Data cdd) {
965 debug.append("\nDB Entry: user=");
966 debug.append(cdd.id);
967 debug.append(",type=");
968 debug.append(cdd.type);
969 debug.append(",expires=");
970 debug.append(Chrono.dateTime(cdd.expires));
971 debug.append(",tag=");
972 debug.append(cdd.tag);
977 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
978 long close = now.getTime() + (days * 86400000);
979 long cexp=cdd.expires.getTime();
981 int daysLeft = days-(int)((close-cexp)/86400000);
982 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
983 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
984 daysLeft,daysLeft==1?"":"s");
989 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
990 if (cred.type==CredDAO.RAW) {
991 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
993 cred.type = CredDAO.BASIC_AUTH_SHA256;
994 cred.other = random.nextInt();
995 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
996 bb.putInt(cred.other);
998 byte[] hash = Hash.hashSHA256(bb.array());
999 cred.cred = ByteBuffer.wrap(hash);
1000 return Result.ok(cred);
1001 } catch (NoSuchAlgorithmException e) {
1002 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1007 } else if (cred.type==CredDAO.FQI) {
1009 return Result.ok(cred);
1011 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1014 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1016 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1019 case CredDAO.BASIC_AUTH_SHA256:
1020 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1021 bb.putInt(orig.other);
1023 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1024 case CredDAO.BASIC_AUTH:
1025 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1028 rv = Result.ok(false);
1030 } catch (NoSuchAlgorithmException e) {
1031 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1038 public static final String APPROVED = "APPROVE";
1039 public static final String REJECT = "REJECT";
1040 public static final String PENDING = "PENDING";
1042 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1043 List<ApprovalDAO.Data> approvals) {
1044 // get the approval policy for the organization
1046 // get the list of approvals with an accept status
1048 // validate the approvals against the policy
1050 // for now check if all approvals are received and return
1051 // SUCCESS/FAILURE/SKIP
1052 boolean bReject = false;
1053 boolean bPending = false;
1055 for (ApprovalDAO.Data approval : approvals) {
1056 if (approval.status.equals(REJECT)) {
1058 } else if (approval.status.equals(PENDING)) {
1063 return Result.err(Status.ERR_Policy,
1064 "Approval Polocy not conformed");
1067 return Result.err(Status.ERR_ActionNotCompleted,
1068 "Required Approvals not received");
1074 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1076 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1077 boolean all = "all".equals(cname);
1078 Result<Void> rv = null;
1080 if (all || NsDAO.TABLE.equals(cname)) {
1081 int[] seg = series(NsDAO.CACHE_SEG);
1082 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1083 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1085 if (all || PermDAO.TABLE.equals(cname)) {
1086 int[] seg = series(PermDAO.CACHE_SEG);
1087 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1088 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1090 if (all || RoleDAO.TABLE.equals(cname)) {
1091 int[] seg = series(RoleDAO.CACHE_SEG);
1092 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1093 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1095 if (all || UserRoleDAO.TABLE.equals(cname)) {
1096 int[] seg = series(UserRoleDAO.CACHE_SEG);
1097 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1098 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1100 if (all || CredDAO.TABLE.equals(cname)) {
1101 int[] seg = series(CredDAO.CACHE_SEG);
1102 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1103 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1105 if (all || CertDAO.TABLE.equals(cname)) {
1106 int[] seg = series(CertDAO.CACHE_SEG);
1107 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1108 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1112 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1117 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1119 if (NsDAO.TABLE.equals(cname)) {
1120 rv = nsDAO.invalidate(segment);
1121 } else if (PermDAO.TABLE.equals(cname)) {
1122 rv = permDAO.invalidate(segment);
1123 } else if (RoleDAO.TABLE.equals(cname)) {
1124 rv = roleDAO.invalidate(segment);
1125 } else if (UserRoleDAO.TABLE.equals(cname)) {
1126 rv = userRoleDAO.invalidate(segment);
1127 } else if (CredDAO.TABLE.equals(cname)) {
1128 rv = credDAO.invalidate(segment);
1129 } else if (CertDAO.TABLE.equals(cname)) {
1130 rv = certDAO.invalidate(segment);
1132 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1137 private int[] series(int max) {
1138 int[] series = new int[max];
1139 for (int i = 0; i < max; ++i)
1144 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1145 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1146 if (userDelegatedFor==null) {
1147 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1148 rldd.put(user, userDelegatedFor);
1150 if (userDelegatedFor.isOKhasData()) {
1151 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1152 if (curr.user.equals(approver) && curr.delegate.equals(user)
1153 && curr.expires.after(new Date())) {
1161 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1162 Boolean b = trans.get(specialLogSlot, null);
1163 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1164 if (specialLog==null) {
1167 b = specialLog.contains(user);
1168 trans.put(specialLogSlot, b);
1174 public static void logEncryptTrace(AuthzTrans trans, String data) {
1176 trans.put(transIDSlot, ti=nextTraceID());
1177 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1180 private synchronized static long nextTraceID() {
1184 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1185 if (specialLog == null) {
1186 specialLog = new HashSet<>();
1188 boolean rc = specialLog.add(id);
1190 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1195 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1196 if (specialLog==null) {
1199 boolean rv = specialLog.remove(id);
1200 if (specialLog.isEmpty()) {
1204 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1211 * Which Types can be moved
1215 public boolean canMove(NsType nsType) {
1230 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1231 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1232 if (rur.isOKhasData()) {
1233 Date now = new Date();
1234 for (UserRoleDAO.Data urdd : rur.value){
1235 if (urdd.expires.after(now)) {
1243 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1244 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1245 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1246 Date now = new Date();
1247 if (urdd.expires.after(now)) {
1254 public int countOwner(AuthzTrans trans, String ns) {
1255 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1256 Date now = new Date();
1258 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1259 if (urdd.expires.after(now)) {
1267 * Return a Unique String, (same string, if it is already unique), with only
1268 * lowercase letters, digits and the '.' character.
1272 * @throws IOException
1274 public static String toUnique(String name) throws IOException {
1275 byte[] from = name.getBytes();
1276 StringBuilder sb = new StringBuilder();
1278 for (int i=0;i<from.length;++i) {
1279 f=(byte)(from[i]); // printables;
1280 sb.append((char)((f>>4)+0x61));
1281 sb.append((char)((f&0x0F)+0x61));
1283 return sb.toString();
1286 public static String fromUnique(String name) throws IOException {
1287 byte[] from = name.getBytes();
1288 StringBuilder sb = new StringBuilder();
1290 for (int i=0;i<from.length;++i) {
1291 c = (char)((from[i]-0x61)<<4);
1292 c |= (from[++i]-0x61);
1295 return sb.toString();