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);
790 String supportedDomain = org.supportedDomain(dd.user);
791 if(supportedDomain==null) {
792 return Result.err(Status.ERR_Denied,
793 "[%s] may not create a delegate for the domain for [%s]",
794 trans.user(), dd.user);
795 } else if(!isGranted(trans, trans.user(), ROOT_NS,DELG,supportedDomain,Question.CREATE)) {
796 return Result.err(Status.ERR_Denied,
797 "[%s] may not create a delegate for [%s]",
798 trans.user(), dd.user);
804 if (!isUser && !isDelegate &&
805 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
806 return Result.err(Status.ERR_Denied,
807 "[%s] may not %s delegates for [%s]", trans.user(),
808 access.name(), dd.user);
812 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
814 } catch (Exception e) {
815 return Result.err(e);
821 * Check (recursively, if necessary), if able to do something based on NS
823 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
824 String ns = nsd.name;
826 // If an ADMIN of the Namespace, then allow
828 Result<List<UserRoleDAO.Data>> rurd;
829 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
830 return Result.ok(nsd);
831 } else if (rurd.status==Result.ERR_Backend) {
832 return Result.err(rurd);
835 // If Specially granted Global Permission
836 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
837 return Result.ok(nsd);
842 int dot = ns.length();
843 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
844 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
846 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
847 } else if (rnsd.status==Result.ERR_Backend) {
848 return Result.err(rnsd);
851 return Result.ok(nsd);
852 } else if (rnsd.status==Result.ERR_Backend) {
853 return Result.err(rnsd);
856 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
864 * Important function - Check internal Permission Schemes for Permission to
873 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
874 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
876 for (PermDAO.Data pd : perms.value) {
877 if (ns.equals(pd.ns)) {
878 if (type.equals(pd.type)) {
879 if (PermEval.evalInstance(pd.instance, instance)) {
880 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
891 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
892 Result<List<CredDAO.Data>> result;
893 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
895 result = credDAO.readIDBAth(trans, user);
900 Result<Date> rv = null;
902 if (result.isEmpty()) {
903 rv = Result.err(Status.ERR_UserNotFound, user);
904 if (willSpecialLog(trans,user)) {
905 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
908 Date now = new Date();
909 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
910 // 9/14/2019. Use TreeSet for sorting, and using only the LAST of a Tagged entry
911 Collection<CredDAO.Data> cddl;
912 if (result.value.size() > 1) {
913 Map<String,CredDAO.Data> mcdd = new TreeMap<>();
917 for (CredDAO.Data rcdd : result.value) {
918 if (rcdd.type==CredDAO.BASIC_AUTH || rcdd.type==CredDAO.BASIC_AUTH_SHA256) {
920 mcdd.put(Integer.toString(++pseudoTag),rcdd);
924 if(cdd==null || cdd.expires.before(rcdd.expires)) {
930 cddl = mcdd.values();
936 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
937 for (CredDAO.Data cdd : cddl) {
938 if (!cdd.id.equals(user)) {
939 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
941 if (cdd.expires.after(now)) {
942 byte[] dbcred = cdd.cred.array();
946 case CredDAO.BASIC_AUTH:
947 byte[] md5=Hash.hashMD5(cred);
948 if (Hash.compareTo(md5,dbcred)==0) {
949 checkLessThanDays(trans,cldays,now,cdd);
950 trans.setTag(cdd.tag);
951 return Result.ok(cdd.expires);
952 } else if (debug!=null) {
956 case CredDAO.BASIC_AUTH_SHA256:
957 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
958 bb.putInt(cdd.other);
960 byte[] hash = Hash.hashSHA256(bb.array());
962 if (Hash.compareTo(hash,dbcred)==0) {
963 checkLessThanDays(trans,cldays,now,cdd);
964 trans.setTag(cdd.tag);
965 return Result.ok(cdd.expires);
966 } else if (debug!=null) {
971 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
973 } catch (NoSuchAlgorithmException e) {
974 trans.error().log(e);
977 if (expired==null || expired.before(cdd.expires)) {
978 expired = cdd.expires;
979 trans.setTag(cdd.tag);
985 // Note: this is only returned if there are no good Credentials
986 rv = Result.err(Status.ERR_Security,
987 "Credentials expired %s",Chrono.utcStamp(expired));
989 if (debug==null && alwaysSpecial) {
990 debug = new StringBuilder();
993 debug.append(trans.env().encryptor().encrypt(new String(cred)));
994 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
999 return Result.err(result);
1001 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
1005 private void load(StringBuilder debug, Data cdd) {
1006 debug.append("\nDB Entry: user=");
1007 debug.append(cdd.id);
1008 debug.append(",type=");
1009 debug.append(cdd.type);
1010 debug.append(",expires=");
1011 debug.append(Chrono.dateTime(cdd.expires));
1012 debug.append(",tag=");
1013 debug.append(cdd.tag);
1018 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1019 long close = now.getTime() + (days * 86400000);
1020 long cexp=cdd.expires.getTime();
1022 int daysLeft = days-(int)((close-cexp)/86400000);
1023 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1024 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
1025 daysLeft,daysLeft==1?"":"s");
1030 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1031 if (cred.type==CredDAO.RAW) {
1032 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1034 cred.type = CredDAO.BASIC_AUTH_SHA256;
1035 cred.other = random.nextInt();
1036 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1037 bb.putInt(cred.other);
1039 byte[] hash = Hash.hashSHA256(bb.array());
1040 cred.cred = ByteBuffer.wrap(hash);
1041 return Result.ok(cred);
1042 } catch (NoSuchAlgorithmException e) {
1043 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1048 } else if (cred.type==CredDAO.FQI) {
1050 return Result.ok(cred);
1052 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1055 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1057 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1060 case CredDAO.BASIC_AUTH_SHA256:
1061 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1062 bb.putInt(orig.other);
1064 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1065 case CredDAO.BASIC_AUTH:
1066 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1069 rv = Result.ok(false);
1071 } catch (NoSuchAlgorithmException e) {
1072 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1079 public static final String APPROVED = "APPROVE";
1080 public static final String REJECT = "REJECT";
1081 public static final String PENDING = "PENDING";
1083 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1084 List<ApprovalDAO.Data> approvals) {
1085 // get the approval policy for the organization
1087 // get the list of approvals with an accept status
1089 // validate the approvals against the policy
1091 // for now check if all approvals are received and return
1092 // SUCCESS/FAILURE/SKIP
1093 boolean bReject = false;
1094 boolean bPending = false;
1096 for (ApprovalDAO.Data approval : approvals) {
1097 if (approval.status.equals(REJECT)) {
1099 } else if (approval.status.equals(PENDING)) {
1104 return Result.err(Status.ERR_Policy,
1105 "Approval Polocy not conformed");
1108 return Result.err(Status.ERR_ActionNotCompleted,
1109 "Required Approvals not received");
1115 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1117 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1118 boolean all = "all".equals(cname);
1119 Result<Void> rv = null;
1121 if (all || NsDAO.TABLE.equals(cname)) {
1122 int[] seg = series(NsDAO.CACHE_SEG);
1123 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1124 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1126 if (all || PermDAO.TABLE.equals(cname)) {
1127 int[] seg = series(PermDAO.CACHE_SEG);
1128 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1129 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1131 if (all || RoleDAO.TABLE.equals(cname)) {
1132 int[] seg = series(RoleDAO.CACHE_SEG);
1133 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1134 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1136 if (all || UserRoleDAO.TABLE.equals(cname)) {
1137 int[] seg = series(UserRoleDAO.CACHE_SEG);
1138 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1139 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1141 if (all || CredDAO.TABLE.equals(cname)) {
1142 int[] seg = series(CredDAO.CACHE_SEG);
1143 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1144 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1146 if (all || CertDAO.TABLE.equals(cname)) {
1147 int[] seg = series(CertDAO.CACHE_SEG);
1148 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1149 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1153 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1158 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1160 if (NsDAO.TABLE.equals(cname)) {
1161 rv = nsDAO.invalidate(segment);
1162 } else if (PermDAO.TABLE.equals(cname)) {
1163 rv = permDAO.invalidate(segment);
1164 } else if (RoleDAO.TABLE.equals(cname)) {
1165 rv = roleDAO.invalidate(segment);
1166 } else if (UserRoleDAO.TABLE.equals(cname)) {
1167 rv = userRoleDAO.invalidate(segment);
1168 } else if (CredDAO.TABLE.equals(cname)) {
1169 rv = credDAO.invalidate(segment);
1170 } else if (CertDAO.TABLE.equals(cname)) {
1171 rv = certDAO.invalidate(segment);
1173 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1178 private int[] series(int max) {
1179 int[] series = new int[max];
1180 for (int i = 0; i < max; ++i)
1185 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1186 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1187 if (userDelegatedFor==null) {
1188 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1189 rldd.put(user, userDelegatedFor);
1191 if (userDelegatedFor.isOKhasData()) {
1192 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1193 if (curr.user.equals(approver) && curr.delegate.equals(user)
1194 && curr.expires.after(new Date())) {
1202 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1203 Boolean b = trans.get(specialLogSlot, null);
1204 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1205 if (specialLog==null) {
1208 b = specialLog.contains(user);
1209 trans.put(specialLogSlot, b);
1215 public static void logEncryptTrace(AuthzTrans trans, String data) {
1217 trans.put(transIDSlot, ti=nextTraceID());
1218 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1221 private synchronized static long nextTraceID() {
1225 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1226 if (specialLog == null) {
1227 specialLog = new HashSet<>();
1229 boolean rc = specialLog.add(id);
1231 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1236 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1237 if (specialLog==null) {
1240 boolean rv = specialLog.remove(id);
1241 if (specialLog.isEmpty()) {
1245 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1252 * Which Types can be moved
1256 public boolean canMove(NsType nsType) {
1271 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1272 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1273 if (rur.isOKhasData()) {
1274 Date now = new Date();
1275 for (UserRoleDAO.Data urdd : rur.value){
1276 if (urdd.expires.after(now)) {
1284 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1285 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1286 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1287 Date now = new Date();
1288 if (urdd.expires.after(now)) {
1295 public int countOwner(AuthzTrans trans, String ns) {
1296 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1297 Date now = new Date();
1299 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1300 if (urdd.expires.after(now)) {
1308 * Return a Unique String, (same string, if it is already unique), with only
1309 * lowercase letters, digits and the '.' character.
1313 * @throws IOException
1315 public static String toUnique(String name) throws IOException {
1316 byte[] from = name.getBytes();
1317 StringBuilder sb = new StringBuilder();
1319 for (int i=0;i<from.length;++i) {
1320 f=(byte)(from[i]); // printables;
1321 sb.append((char)((f>>4)+0x61));
1322 sb.append((char)((f&0x0F)+0x61));
1324 return sb.toString();
1327 public static String fromUnique(String name) throws IOException {
1328 byte[] from = name.getBytes();
1329 StringBuilder sb = new StringBuilder();
1331 for (int i=0;i<from.length;++i) {
1332 c = (char)((from[i]-0x61)<<4);
1333 c |= (from[++i]-0x61);
1336 return sb.toString();