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);
497 if (type.type == nsd.type) {
498 return Result.ok(nsd);
500 int dot = str.lastIndexOf('.');
503 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
505 return deriveFirstNsForType(trans, str.substring(0, dot),type);
509 int dot = str.lastIndexOf('.');
512 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
514 return deriveFirstNsForType(trans, str.substring(0, dot),type);
518 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
521 public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
522 Result<NsDAO.Data> ndd = deriveNs(trans, child);
524 NsSplit nss = new NsSplit(ndd.value, child);
526 return Result.ok(nss);
528 return Result.err(Status.ERR_NsNotFound,
529 "Cannot split [%s] into valid namespace elements",
533 return Result.err(ndd);
537 * Translate an ID into it's domain
539 * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
544 public static String domain2ns(String id) {
545 int at = id.indexOf('@');
547 String[] domain = id.substring(at + 1).split("\\.");
548 StringBuilder ns = new StringBuilder(id.length());
549 boolean first = true;
550 for (int i = domain.length - 1; i >= 0; --i) {
556 ns.append(domain[i]);
558 return ns.toString();
566 * Validate Namespace of ID@Domain
568 * Namespace is reverse order of Domain.
574 public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
575 // Take domain, reverse order, and check on NS
577 if (id.indexOf('@')<0) { // it's already an ns, not an ID
582 if (ns.length() > 0) {
583 if (!trans.org().getDomain().equals(ns)) {
584 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
585 if (rlnsd.isOKhasData()) {
586 return Result.ok(rlnsd.value.get(0));
590 return Result.err(Status.ERR_NsNotFound,
591 "A Namespace is not available for %s", id);
594 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
595 // <ns>.access|:role:<role name>|<read|write>
596 String ns = ndd.name;
599 if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
600 return Result.ok(ndd);
602 if ((last = ns.lastIndexOf('.')) >= 0) {
603 ns = ns.substring(0, last);
606 // com.att.aaf.ns|:<client ns>:ns|<access>
607 // AAF-724 - Make consistent response for May User", and not take the
608 // last check... too confusing.
609 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
612 } else if (rv.status==Result.ERR_Backend) {
613 return Result.err(rv);
615 return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
616 user, access.name(), ndd.name);
620 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
621 if(trans.user().equals(rdd.ns)) {
622 return Result.ok((NsDAO.Data)null);
624 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
626 return mayUser(trans, user, rnsd.value, rdd, access);
631 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
632 // 1) Is User in the Role?
633 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
634 if (rurd.isOKhasData()) {
635 return Result.ok(ndd);
638 String roleInst = ":role:" + rdd.name;
639 // <ns>.access|:role:<role name>|<read|write>
643 if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
644 return Result.ok(ndd);
646 if ((last = ns.lastIndexOf('.')) >= 0) {
647 ns = ns.substring(0, last);
651 // Check if Access by Global Role perm
652 // com.att.aaf.ns|:<client ns>:role:name|<access>
653 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
654 + rdd.ns + roleInst, access.name());
657 } else if (rnsd.status==Result.ERR_Backend) {
658 return Result.err(rnsd);
661 // Check if Access to Whole NS
662 // AAF-724 - Make consistent response for May User", and not take the
663 // last check... too confusing.
664 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd,
665 ":" + rdd.ns + ":ns", access.name());
668 } else if (rnsd.status==Result.ERR_Backend) {
669 return Result.err(rnsd);
671 return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
672 user, access.name(), rdd.fullName());
677 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
678 if(pdd.ns.indexOf('@')>-1) {
679 if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
680 NsDAO.Data ndd = new NsDAO.Data();
682 ndd.type = NsDAO.USER;
684 return Result.ok(ndd);
686 return Result.err(Result.ERR_Security,"Only a User may modify User");
689 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
691 return mayUser(trans, user, rnsd.value, pdd, access);
696 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
697 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
698 return Result.ok(ndd);
700 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
701 // <ns>.access|:role:<role name>|<read|write>
702 String ns = ndd.name;
705 if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
706 return Result.ok(ndd);
708 if ((last = ns.lastIndexOf('.')) >= 0) {
709 ns = ns.substring(0, last);
713 // Check if Access by NS perm
714 // com.att.aaf.ns|:<client ns>:role:name|<access>
715 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
718 } else if (rnsd.status==Result.ERR_Backend) {
719 return Result.err(rnsd);
722 // Check if Access to Whole NS
723 // AAF-724 - Make consistent response for May User", and not take the
724 // last check... too confusing.
725 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
729 return Result.err(Status.ERR_Denied,
730 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
731 pdd.fullType(), pdd.instance, pdd.action);
736 public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
738 Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
739 if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":" + rnsd.value.name + ":ns", access.name()).isOK()) {
742 boolean isUser = trans.user().equals(dd.user);
743 boolean isDelegate = dd.delegate != null
744 && (dd.user.equals(dd.delegate) || trans.user().equals(
746 Organization org = trans.org();
749 if (org.getIdentity(trans, dd.user) == null) {
750 return Result.err(Status.ERR_UserNotFound,
751 "[%s] is not a user in the company database.",
754 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
755 return Result.err(Status.ERR_UserNotFound,
756 "[%s] is not a user in the company database.",
759 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
760 return Result.err(Status.ERR_BadData,
761 "[%s] cannot be a delegate for self", dd.user);
763 if (!isUser && !isGranted(trans, trans.user(), ROOT_NS,DELG,
764 org.getDomain(), Question.CREATE)) {
765 return Result.err(Status.ERR_Denied,
766 "[%s] may not create a delegate for [%s]",
767 trans.user(), dd.user);
772 if (!isUser && !isDelegate &&
773 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
774 return Result.err(Status.ERR_Denied,
775 "[%s] may not %s delegates for [%s]", trans.user(),
776 access.name(), dd.user);
780 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
782 } catch (Exception e) {
783 return Result.err(e);
789 * Check (recursively, if necessary), if able to do something based on NS
791 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
792 String ns = nsd.name;
794 // If an ADMIN of the Namespace, then allow
796 Result<List<UserRoleDAO.Data>> rurd;
797 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
798 return Result.ok(nsd);
799 } else if (rurd.status==Result.ERR_Backend) {
800 return Result.err(rurd);
803 // If Specially granted Global Permission
804 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
805 return Result.ok(nsd);
810 int dot = ns.length();
811 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
812 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
814 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
815 } else if (rnsd.status==Result.ERR_Backend) {
816 return Result.err(rnsd);
819 return Result.ok(nsd);
820 } else if (rnsd.status==Result.ERR_Backend) {
821 return Result.err(rnsd);
824 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
832 * Important function - Check internal Permission Schemes for Permission to
841 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
842 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
844 for (PermDAO.Data pd : perms.value) {
845 if (ns.equals(pd.ns)) {
846 if (type.equals(pd.type)) {
847 if (PermEval.evalInstance(pd.instance, instance)) {
848 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
859 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
860 Result<List<CredDAO.Data>> result;
861 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
863 result = credDAO.readID(trans, user);
868 Result<Date> rv = null;
870 if (result.isEmpty()) {
871 rv = Result.err(Status.ERR_UserNotFound, user);
872 if (willSpecialLog(trans,user)) {
873 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
876 Date now = new Date();
877 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
878 List<CredDAO.Data> cddl;
879 if (result.value.size() > 1) {
880 cddl = new ArrayList<>(result.value.size());
881 for (CredDAO.Data old : result.value) {
882 if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
887 Collections.sort(cddl, (a, b) -> b.expires.compareTo(a.expires));
894 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
895 for (CredDAO.Data cdd : cddl) {
896 if (!cdd.id.equals(user)) {
897 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
899 if (cdd.expires.after(now)) {
900 byte[] dbcred = cdd.cred.array();
904 case CredDAO.BASIC_AUTH:
905 byte[] md5=Hash.hashMD5(cred);
906 if (Hash.compareTo(md5,dbcred)==0) {
907 checkLessThanDays(trans,cldays,now,cdd);
908 trans.setTag(cdd.tag);
909 return Result.ok(cdd.expires);
910 } else if (debug!=null) {
914 case CredDAO.BASIC_AUTH_SHA256:
915 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
916 bb.putInt(cdd.other);
918 byte[] hash = Hash.hashSHA256(bb.array());
920 if (Hash.compareTo(hash,dbcred)==0) {
921 checkLessThanDays(trans,cldays,now,cdd);
922 trans.setTag(cdd.tag);
923 return Result.ok(cdd.expires);
924 } else if (debug!=null) {
929 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
931 } catch (NoSuchAlgorithmException e) {
932 trans.error().log(e);
935 if (expired==null || expired.before(cdd.expires)) {
936 expired = cdd.expires;
937 trans.setTag(cdd.tag);
943 // Note: this is only returned if there are no good Credentials
944 rv = Result.err(Status.ERR_Security,
945 "Credentials expired %s",Chrono.utcStamp(expired));
947 if (debug==null && alwaysSpecial) {
948 debug = new StringBuilder();
951 debug.append(trans.env().encryptor().encrypt(new String(cred)));
952 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
957 return Result.err(result);
959 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
963 private void load(StringBuilder debug, Data cdd) {
964 debug.append("\nDB Entry: user=");
965 debug.append(cdd.id);
966 debug.append(",type=");
967 debug.append(cdd.type);
968 debug.append(",expires=");
969 debug.append(Chrono.dateTime(cdd.expires));
970 debug.append(",tag=");
971 debug.append(cdd.tag);
976 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
977 long close = now.getTime() + (days * 86400000);
978 long cexp=cdd.expires.getTime();
980 int daysLeft = days-(int)((close-cexp)/86400000);
981 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
982 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
983 daysLeft,daysLeft==1?"":"s");
988 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
989 if (cred.type==CredDAO.RAW) {
990 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
992 cred.type = CredDAO.BASIC_AUTH_SHA256;
993 cred.other = random.nextInt();
994 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
995 bb.putInt(cred.other);
997 byte[] hash = Hash.hashSHA256(bb.array());
998 cred.cred = ByteBuffer.wrap(hash);
999 return Result.ok(cred);
1000 } catch (NoSuchAlgorithmException e) {
1001 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1006 } else if (cred.type==CredDAO.FQI) {
1008 return Result.ok(cred);
1010 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1013 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1015 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1018 case CredDAO.BASIC_AUTH_SHA256:
1019 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1020 bb.putInt(orig.other);
1022 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1023 case CredDAO.BASIC_AUTH:
1024 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1027 rv = Result.ok(false);
1029 } catch (NoSuchAlgorithmException e) {
1030 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1037 public static final String APPROVED = "APPROVE";
1038 public static final String REJECT = "REJECT";
1039 public static final String PENDING = "PENDING";
1041 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1042 List<ApprovalDAO.Data> approvals) {
1043 // get the approval policy for the organization
1045 // get the list of approvals with an accept status
1047 // validate the approvals against the policy
1049 // for now check if all approvals are received and return
1050 // SUCCESS/FAILURE/SKIP
1051 boolean bReject = false;
1052 boolean bPending = false;
1054 for (ApprovalDAO.Data approval : approvals) {
1055 if (approval.status.equals(REJECT)) {
1057 } else if (approval.status.equals(PENDING)) {
1062 return Result.err(Status.ERR_Policy,
1063 "Approval Polocy not conformed");
1066 return Result.err(Status.ERR_ActionNotCompleted,
1067 "Required Approvals not received");
1073 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1075 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1076 boolean all = "all".equals(cname);
1077 Result<Void> rv = null;
1079 if (all || NsDAO.TABLE.equals(cname)) {
1080 int[] seg = series(NsDAO.CACHE_SEG);
1081 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1082 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1084 if (all || PermDAO.TABLE.equals(cname)) {
1085 int[] seg = series(PermDAO.CACHE_SEG);
1086 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1087 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1089 if (all || RoleDAO.TABLE.equals(cname)) {
1090 int[] seg = series(RoleDAO.CACHE_SEG);
1091 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1092 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1094 if (all || UserRoleDAO.TABLE.equals(cname)) {
1095 int[] seg = series(UserRoleDAO.CACHE_SEG);
1096 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1097 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1099 if (all || CredDAO.TABLE.equals(cname)) {
1100 int[] seg = series(CredDAO.CACHE_SEG);
1101 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1102 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1104 if (all || CertDAO.TABLE.equals(cname)) {
1105 int[] seg = series(CertDAO.CACHE_SEG);
1106 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1107 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1111 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1116 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1118 if (NsDAO.TABLE.equals(cname)) {
1119 rv = nsDAO.invalidate(segment);
1120 } else if (PermDAO.TABLE.equals(cname)) {
1121 rv = permDAO.invalidate(segment);
1122 } else if (RoleDAO.TABLE.equals(cname)) {
1123 rv = roleDAO.invalidate(segment);
1124 } else if (UserRoleDAO.TABLE.equals(cname)) {
1125 rv = userRoleDAO.invalidate(segment);
1126 } else if (CredDAO.TABLE.equals(cname)) {
1127 rv = credDAO.invalidate(segment);
1128 } else if (CertDAO.TABLE.equals(cname)) {
1129 rv = certDAO.invalidate(segment);
1131 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1136 private int[] series(int max) {
1137 int[] series = new int[max];
1138 for (int i = 0; i < max; ++i)
1143 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1144 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1145 if (userDelegatedFor==null) {
1146 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1147 rldd.put(user, userDelegatedFor);
1149 if (userDelegatedFor.isOKhasData()) {
1150 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1151 if (curr.user.equals(approver) && curr.delegate.equals(user)
1152 && curr.expires.after(new Date())) {
1160 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1161 Boolean b = trans.get(specialLogSlot, null);
1162 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1163 if (specialLog==null) {
1166 b = specialLog.contains(user);
1167 trans.put(specialLogSlot, b);
1173 public static void logEncryptTrace(AuthzTrans trans, String data) {
1175 trans.put(transIDSlot, ti=nextTraceID());
1176 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1179 private synchronized static long nextTraceID() {
1183 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1184 if (specialLog == null) {
1185 specialLog = new HashSet<>();
1187 boolean rc = specialLog.add(id);
1189 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1194 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1195 if (specialLog==null) {
1198 boolean rv = specialLog.remove(id);
1199 if (specialLog.isEmpty()) {
1203 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1210 * Which Types can be moved
1214 public boolean canMove(NsType nsType) {
1229 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1230 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1231 if (rur.isOKhasData()) {
1232 Date now = new Date();
1233 for (UserRoleDAO.Data urdd : rur.value){
1234 if (urdd.expires.after(now)) {
1242 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1243 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1244 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1245 Date now = new Date();
1246 if (urdd.expires.after(now)) {
1253 public int countOwner(AuthzTrans trans, String ns) {
1254 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1255 Date now = new Date();
1257 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1258 if (urdd.expires.after(now)) {
1266 * Return a Unique String, (same string, if it is already unique), with only
1267 * lowercase letters, digits and the '.' character.
1271 * @throws IOException
1273 public static String toUnique(String name) throws IOException {
1274 byte[] from = name.getBytes();
1275 StringBuilder sb = new StringBuilder();
1277 for (int i=0;i<from.length;++i) {
1278 f=(byte)(from[i]); // printables;
1279 sb.append((char)((f>>4)+0x61));
1280 sb.append((char)((f&0x0F)+0x61));
1282 return sb.toString();
1285 public static String fromUnique(String name) throws IOException {
1286 byte[] from = name.getBytes();
1287 StringBuilder sb = new StringBuilder();
1289 for (int i=0;i<from.length;++i) {
1290 c = (char)((from[i]-0x61)<<4);
1291 c |= (from[++i]-0x61);
1294 return sb.toString();