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;
35 import java.util.TreeSet;
37 import org.onap.aaf.auth.common.Define;
38 import org.onap.aaf.auth.dao.AbsCassDAO;
39 import org.onap.aaf.auth.dao.CachedDAO;
40 import org.onap.aaf.auth.dao.DAOException;
41 import org.onap.aaf.auth.dao.cached.CachedCertDAO;
42 import org.onap.aaf.auth.dao.cached.CachedCredDAO;
43 import org.onap.aaf.auth.dao.cached.CachedNSDAO;
44 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
45 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
46 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
47 import org.onap.aaf.auth.dao.cached.FileGetter;
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 SecureRandom 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 // Create if aaf_file_cred exists with file
206 FileGetter.singleton(trans.env().access());
207 credDAO = new CachedCredDAO(new CredDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
208 certDAO = new CachedCertDAO(new CertDAO(trans, historyDAO, cacheInfoDAO), cacheInfoDAO, expiresIn);
210 locateDAO = new LocateDAO(trans,historyDAO);
211 futureDAO = new FutureDAO(trans, historyDAO);
212 delegateDAO = new DelegateDAO(trans, historyDAO);
213 approvalDAO = new ApprovalDAO(trans, historyDAO);
215 if (specialLogSlot==null) {
216 specialLogSlot = trans.slot(AuthzTransFilter.SPECIAL_LOG_SLOT);
219 if (transIDSlot==null) {
220 transIDSlot = trans.slot(AuthzTransFilter.TRANS_ID_SLOT);
223 AbsCassDAO.primePSIs(trans);
225 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
227 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
231 * Note: This Constructor created for JUNIT Purposes. Do not use otherwise.
233 public Question(AuthzTrans trans, HistoryDAO historyDAO, CacheInfoDAO cacheInfoDAO,
234 CachedNSDAO nsDAO, CachedPermDAO permDAO, CachedRoleDAO roleDAO,
235 CachedUserRoleDAO userRoleDAO, CachedCredDAO credDAO, CachedCertDAO certDAO,
236 LocateDAO locateDAO,FutureDAO futureDAO, DelegateDAO delegateDAO,
237 ApprovalDAO approvalDAO ) {
238 this.historyDAO = historyDAO;
239 this.cacheInfoDAO = cacheInfoDAO;
241 this.permDAO = permDAO;
242 this.roleDAO = roleDAO;
243 this.userRoleDAO = userRoleDAO;
244 this.credDAO = credDAO;
245 this.certDAO = certDAO;
246 this.locateDAO = locateDAO;
247 this.futureDAO = futureDAO;
248 this.delegateDAO = delegateDAO;
249 this.approvalDAO = approvalDAO;
251 cldays = Integer.parseInt(trans.getProperty(Config.AAF_CRED_WARN_DAYS, Config.AAF_CRED_WARN_DAYS_DFT));
252 alwaysSpecial = Boolean.parseBoolean(trans.getProperty("aaf_always_special", Boolean.FALSE.toString()));
255 public void startTimers(AuthzEnv env) {
256 // Only want to aggressively cleanse User related Caches... The others,
257 // just normal refresh
258 CachedDAO.startCleansing(env, credDAO, userRoleDAO);
259 CachedDAO.startRefresh(env, cacheInfoDAO);
262 public void close(AuthzTrans trans) {
263 historyDAO.close(trans);
264 cacheInfoDAO.close(trans);
266 permDAO.close(trans);
267 roleDAO.close(trans);
268 userRoleDAO.close(trans);
269 credDAO.close(trans);
270 certDAO.close(trans);
271 delegateDAO.close(trans);
272 futureDAO.close(trans);
273 approvalDAO.close(trans);
276 public Result<PermDAO.Data> permFrom(AuthzTrans trans, String type, String instance, String action) {
277 if(type.indexOf('@') >= 0) {
278 int colon = type.indexOf(':');
280 PermDAO.Data pdd = new PermDAO.Data();
281 pdd.ns = type.substring(0, colon);
282 pdd.type = type.substring(colon+1);
283 pdd.instance = instance;
286 return Result.ok(pdd);
288 return Result.err(Result.ERR_BadData,"Could not extract ns and type from " + type);
291 Result<NsDAO.Data> rnd = deriveNs(trans, type);
293 return Result.ok(new PermDAO.Data(new NsSplit(rnd.value, type),
296 return Result.err(rnd);
304 * Because this call is frequently called internally, AND because we already
305 * look for it in the initial Call, we cache within the Transaction
311 public Result<List<PermDAO.Data>> getPermsByUser(AuthzTrans trans, String user, boolean lookup) {
312 return PermLookup.get(trans, this, user).getPerms(lookup);
315 public Result<List<PermDAO.Data>> getPermsByUserFromRolesFilter(AuthzTrans trans, String user, String forUser) {
316 PermLookup plUser = PermLookup.get(trans, this, user);
317 Result<Set<String>> plPermNames = plUser.getPermNames();
318 if (plPermNames.notOK()) {
319 return Result.err(plPermNames);
323 if (forUser.equals(user)) {
326 // Setup a TreeSet to check on Namespaces to
327 nss = new TreeSet<>();
328 PermLookup fUser = PermLookup.get(trans, this, forUser);
329 Result<Set<String>> forUpn = fUser.getPermNames();
330 if (forUpn.notOK()) {
331 return Result.err(forUpn);
334 for (String pn : forUpn.value) {
335 Result<String[]> decoded = PermDAO.Data.decodeToArray(trans, this, pn);
336 if (decoded.isOKhasData()) {
337 nss.add(decoded.value[0]);
339 trans.error().log(pn,", derived from a Role, is invalid:",decoded.errorString());
344 List<PermDAO.Data> rlpUser = new ArrayList<>();
345 Result<PermDAO.Data> rpdd;
347 for (String pn : plPermNames.value) {
348 rpdd = PermDAO.Data.decode(trans, this, pn);
349 if (rpdd.isOKhasData()) {
351 if (nss==null || nss.contains(pdd.ns)) {
355 trans.error().log(pn,", derived from a Role, is invalid. Run Data Cleanup:",rpdd.errorString());
358 return Result.ok(rlpUser);
361 public Result<List<PermDAO.Data>> getPermsByType(AuthzTrans trans, String type) {
362 if(type.indexOf('@') >= 0) {
363 int colon = type.indexOf(':');
365 return permDAO.readByType(trans, type.substring(0, colon),type.substring(colon+1));
367 return Result.err(Result.ERR_BadData, "%s is malformed",type);
370 Result<NsSplit> nss = deriveNsSplit(trans, type);
372 return Result.err(nss);
374 return permDAO.readByType(trans, nss.value.ns, nss.value.name);
378 public Result<List<PermDAO.Data>> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
379 if(type.indexOf('@') >= 0) {
380 int colon = type.indexOf(':');
382 return permDAO.read(trans, type.substring(0, colon),type.substring(colon+1), instance,action);
384 return Result.err(Result.ERR_BadData, "%s is malformed",type);
387 Result<NsSplit> nss = deriveNsSplit(trans, type);
389 return Result.err(nss);
392 return permDAO.read(trans, nss.value.ns, nss.value.name, instance,action);
396 public Result<List<PermDAO.Data>> getPermsByRole(AuthzTrans trans, String role, boolean lookup) {
397 Result<NsSplit> nss = deriveNsSplit(trans, role);
399 return Result.err(nss);
402 Result<List<RoleDAO.Data>> rlrd = roleDAO.read(trans, nss.value.ns,
404 if (rlrd.notOKorIsEmpty()) {
405 return Result.err(rlrd);
407 // Using Set to avoid duplicates
408 Set<String> permNames = new HashSet<>();
409 if (rlrd.isOKhasData()) {
410 for (RoleDAO.Data drr : rlrd.value) {
411 permNames.addAll(drr.perms(false));
415 // Note: It should be ok for a Valid user to have no permissions -
416 // Jonathan 8/12/2013
417 List<PermDAO.Data> perms = new ArrayList<>();
418 for (String perm : permNames) {
419 Result<PermDAO.Data> pr = PermDAO.Data.decode(trans, this, perm);
421 return Result.err(pr);
425 Result<List<PermDAO.Data>> rlpd = permDAO.read(trans, pr.value);
426 if (rlpd.isOKhasData()) {
427 for (PermDAO.Data pData : rlpd.value) {
436 return Result.ok(perms);
439 public Result<List<RoleDAO.Data>> getRolesByName(AuthzTrans trans, String role) {
440 if(role.startsWith(trans.user()) ) {
441 if(role.endsWith(":user")) {
442 return roleDAO.read(trans,trans.user(), "user");
444 return Result.err(Result.ERR_BadData,"%s is a badly formatted role",role);
447 Result<NsSplit> nss = deriveNsSplit(trans, role);
449 return Result.err(nss);
451 String r = nss.value.name;
452 if (r.endsWith(".*")) { // do children Search
453 return roleDAO.readChildren(trans, nss.value.ns,
454 r.substring(0, r.length() - 2));
455 } else if (ASTERIX.equals(r)) {
456 return roleDAO.readChildren(trans, nss.value.ns, ASTERIX);
458 return roleDAO.read(trans, nss.value.ns, r);
465 * Given a Child Namespace, figure out what the best Namespace parent is.
467 * For instance, if in the NS table, the parent "org.osaaf" exists, but not
468 * "org.osaaf.child" or "org.osaaf.a.b.c", then passing in either
469 * "org.osaaf.child" or "org.osaaf.a.b.c" will return "org.osaaf"
471 * Uses recursive search on Cached DAO data
477 public Result<NsDAO.Data> deriveNs(AuthzTrans trans, String child) {
478 Result<List<NsDAO.Data>> r = nsDAO.read(trans, child);
480 if (r.isOKhasData()) {
481 return Result.ok(r.value.get(0));
483 int dot = child.lastIndexOf('.');
485 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", child);
487 return deriveNs(trans, child.substring(0, dot));
492 public Result<NsDAO.Data> deriveFirstNsForType(AuthzTrans trans, String str, NsType type) {
495 for (String lookup = str;!".".equals(lookup) && lookup!=null;) {
496 Result<List<NsDAO.Data>> rld = nsDAO.read(trans, lookup);
497 if (rld.isOKhasData()) {
498 nsd=rld.value.get(0);
500 if (type.type == nsd.type) {
501 return Result.ok(nsd);
503 int dot = str.lastIndexOf('.');
506 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
508 return deriveFirstNsForType(trans, str.substring(0, dot),type);
512 int dot = str.lastIndexOf('.');
515 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
517 return deriveFirstNsForType(trans, str.substring(0, dot),type);
521 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
524 public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
525 Result<NsDAO.Data> ndd = deriveNs(trans, child);
527 NsSplit nss = new NsSplit(ndd.value, child);
529 return Result.ok(nss);
531 return Result.err(Status.ERR_NsNotFound,
532 "Cannot split [%s] into valid namespace elements",
536 return Result.err(ndd);
540 * Translate an ID into it's domain
542 * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
547 public static String domain2ns(String id) {
548 int at = id.indexOf('@');
550 String[] domain = id.substring(at + 1).split("\\.");
551 StringBuilder ns = new StringBuilder(id.length());
552 boolean first = true;
553 for (int i = domain.length - 1; i >= 0; --i) {
559 ns.append(domain[i]);
561 return ns.toString();
569 * Validate Namespace of ID@Domain
571 * Namespace is reverse order of Domain.
577 public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
578 // Take domain, reverse order, and check on NS
580 if (id.indexOf('@')<0) { // it's already an ns, not an ID
585 if (ns.length() > 0) {
586 if (!trans.org().getDomain().equals(ns)) {
587 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
588 if (rlnsd.isOKhasData()) {
589 return Result.ok(rlnsd.value.get(0));
593 return Result.err(Status.ERR_NsNotFound,
594 "A Namespace is not available for %s", id);
597 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
598 // <ns>.access|:role:<role name>|<read|write>
599 String ns = ndd.name;
600 boolean isRoot = ns.startsWith(Define.ROOT_NS());
603 if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
604 return Result.ok(ndd);
609 if ((last = ns.lastIndexOf('.')) >= 0) {
610 ns = ns.substring(0, last);
614 // SAFETY - Do not allow these when NS is Root
616 // com.att.aaf.ns|:<client ns>:ns|<access>
617 // AAF-724 - Make consistent response for May User", and not take the
618 // last check... too confusing.
619 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
622 } else if (rv.status==Result.ERR_Backend) {
623 return Result.err(rv);
626 return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
627 user, access.name(), ndd.name);
631 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
632 if(trans.user().equals(rdd.ns)) {
633 return Result.ok((NsDAO.Data)null);
635 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
637 return mayUser(trans, user, rnsd.value, rdd, access);
642 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
643 // 1) For "read", Is User in the Role is enough
644 if(Access.read.equals(access)) {
645 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
646 if (rurd.isOKhasData()) {
647 return Result.ok(ndd);
651 String roleInst = ":role:" + rdd.name;
652 // <ns>.access|:role:<role name>|<read|write>
654 boolean isRoot = ns.startsWith(Define.ROOT_NS());
657 if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
658 return Result.ok(ndd);
663 if ((last = ns.lastIndexOf('.')) >= 0) {
664 ns = ns.substring(0, last);
668 // SAFETY - Do not allow these when NS is Root
670 // Check if Access by Global Role perm
671 // com.att.aaf.ns|:<client ns>:role:name|<access>
672 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
673 + rdd.ns + roleInst, access.name());
676 } else if (rnsd.status==Result.ERR_Backend) {
677 return Result.err(rnsd);
680 // Check if Access to Whole NS
681 // AAF-724 - Make consistent response for May User", and not take the
682 // last check... too confusing.
683 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd,
684 ":" + rdd.ns + ":ns", access.name());
687 } else if (rnsd.status==Result.ERR_Backend) {
688 return Result.err(rnsd);
691 return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
692 user, access.name(), rdd.fullName());
695 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
696 if(pdd.ns.indexOf('@')>-1) {
697 if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
698 NsDAO.Data ndd = new NsDAO.Data();
700 ndd.type = NsDAO.USER;
702 return Result.ok(ndd);
704 return Result.err(Result.ERR_Security,"Only a User may modify User");
707 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
709 return mayUser(trans, user, rnsd.value, pdd, access);
714 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
715 // Most common occurrence... if granted Permission
716 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
717 return Result.ok(ndd);
720 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
721 // <ns>.access|:role:<role name>|<read|write>
722 String ns = ndd.name;
723 boolean isRoot = ns.startsWith(Define.ROOT_NS());
726 if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
727 return Result.ok(ndd);
732 if ((last = ns.lastIndexOf('.')) >= 0) {
733 ns = ns.substring(0, last);
737 // SAFETY - Do not allow these when NS is Root
739 // Check if Access by NS perm
740 // com.att.aaf.ns|:<client ns>:role:name|<access>
741 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
744 } else if (rnsd.status==Result.ERR_Backend) {
745 return Result.err(rnsd);
748 // Check if Access to Whole NS
749 // AAF-724 - Make consistent response for May User", and not take the
750 // last check... too confusing.
751 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
756 return Result.err(Status.ERR_Denied,
757 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
758 pdd.fullType(), pdd.instance, pdd.action);
761 public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
763 Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
764 if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":" + rnsd.value.name + ":ns", access.name()).isOK()) {
767 boolean isUser = trans.user().equals(dd.user);
768 boolean isDelegate = dd.delegate != null
769 && (dd.user.equals(dd.delegate) || trans.user().equals(
771 Organization org = trans.org();
774 if (org.getIdentity(trans, dd.user) == null) {
775 return Result.err(Status.ERR_UserNotFound,
776 "[%s] is not a user in the company database.",
779 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
780 return Result.err(Status.ERR_UserNotFound,
781 "[%s] is not a user in the company database.",
784 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
785 return Result.err(Status.ERR_BadData,
786 "[%s] cannot be a delegate for self", dd.user);
788 if (!isUser && !isGranted(trans, trans.user(), ROOT_NS,DELG,
789 org.getDomain(), Question.CREATE)) {
790 return Result.err(Status.ERR_Denied,
791 "[%s] may not create a delegate for [%s]",
792 trans.user(), dd.user);
797 if (!isUser && !isDelegate &&
798 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
799 return Result.err(Status.ERR_Denied,
800 "[%s] may not %s delegates for [%s]", trans.user(),
801 access.name(), dd.user);
805 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
807 } catch (Exception e) {
808 return Result.err(e);
814 * Check (recursively, if necessary), if able to do something based on NS
816 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
817 String ns = nsd.name;
819 // If an ADMIN of the Namespace, then allow
821 Result<List<UserRoleDAO.Data>> rurd;
822 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
823 return Result.ok(nsd);
824 } else if (rurd.status==Result.ERR_Backend) {
825 return Result.err(rurd);
828 // If Specially granted Global Permission
829 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
830 return Result.ok(nsd);
835 int dot = ns.length();
836 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
837 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
839 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
840 } else if (rnsd.status==Result.ERR_Backend) {
841 return Result.err(rnsd);
844 return Result.ok(nsd);
845 } else if (rnsd.status==Result.ERR_Backend) {
846 return Result.err(rnsd);
849 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
857 * Important function - Check internal Permission Schemes for Permission to
866 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
867 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
869 for (PermDAO.Data pd : perms.value) {
870 if (ns.equals(pd.ns)) {
871 if (type.equals(pd.type)) {
872 if (PermEval.evalInstance(pd.instance, instance)) {
873 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
884 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
885 Result<List<CredDAO.Data>> result;
886 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
888 result = credDAO.readIDBAth(trans, user);
893 Result<Date> rv = null;
895 if (result.isEmpty()) {
896 rv = Result.err(Status.ERR_UserNotFound, user);
897 if (willSpecialLog(trans,user)) {
898 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
901 Date now = new Date();
902 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
903 List<CredDAO.Data> cddl;
904 if (result.value.size() > 1) {
905 cddl = new ArrayList<>(result.value.size());
906 for (CredDAO.Data old : result.value) {
907 if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
912 Collections.sort(cddl, (a, b) -> b.expires.compareTo(a.expires));
919 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
920 for (CredDAO.Data cdd : cddl) {
921 if (!cdd.id.equals(user)) {
922 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
924 if (cdd.expires.after(now)) {
925 byte[] dbcred = cdd.cred.array();
929 case CredDAO.BASIC_AUTH:
930 byte[] md5=Hash.hashMD5(cred);
931 if (Hash.compareTo(md5,dbcred)==0) {
932 checkLessThanDays(trans,cldays,now,cdd);
933 trans.setTag(cdd.tag);
934 return Result.ok(cdd.expires);
935 } else if (debug!=null) {
939 case CredDAO.BASIC_AUTH_SHA256:
940 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
941 bb.putInt(cdd.other);
943 byte[] hash = Hash.hashSHA256(bb.array());
945 if (Hash.compareTo(hash,dbcred)==0) {
946 checkLessThanDays(trans,cldays,now,cdd);
947 trans.setTag(cdd.tag);
948 return Result.ok(cdd.expires);
949 } else if (debug!=null) {
954 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
956 } catch (NoSuchAlgorithmException e) {
957 trans.error().log(e);
960 if (expired==null || expired.before(cdd.expires)) {
961 expired = cdd.expires;
962 trans.setTag(cdd.tag);
968 // Note: this is only returned if there are no good Credentials
969 rv = Result.err(Status.ERR_Security,
970 "Credentials expired %s",Chrono.utcStamp(expired));
972 if (debug==null && alwaysSpecial) {
973 debug = new StringBuilder();
976 debug.append(trans.env().encryptor().encrypt(new String(cred)));
977 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
982 return Result.err(result);
984 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
988 private void load(StringBuilder debug, Data cdd) {
989 debug.append("\nDB Entry: user=");
990 debug.append(cdd.id);
991 debug.append(",type=");
992 debug.append(cdd.type);
993 debug.append(",expires=");
994 debug.append(Chrono.dateTime(cdd.expires));
995 debug.append(",tag=");
996 debug.append(cdd.tag);
1001 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1002 long close = now.getTime() + (days * 86400000);
1003 long cexp=cdd.expires.getTime();
1005 int daysLeft = days-(int)((close-cexp)/86400000);
1006 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1007 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
1008 daysLeft,daysLeft==1?"":"s");
1013 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1014 if (cred.type==CredDAO.RAW) {
1015 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1017 cred.type = CredDAO.BASIC_AUTH_SHA256;
1018 cred.other = random.nextInt();
1019 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1020 bb.putInt(cred.other);
1022 byte[] hash = Hash.hashSHA256(bb.array());
1023 cred.cred = ByteBuffer.wrap(hash);
1024 return Result.ok(cred);
1025 } catch (NoSuchAlgorithmException e) {
1026 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1031 } else if (cred.type==CredDAO.FQI) {
1033 return Result.ok(cred);
1035 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1038 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1040 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1043 case CredDAO.BASIC_AUTH_SHA256:
1044 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1045 bb.putInt(orig.other);
1047 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1048 case CredDAO.BASIC_AUTH:
1049 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1052 rv = Result.ok(false);
1054 } catch (NoSuchAlgorithmException e) {
1055 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1062 public static final String APPROVED = "APPROVE";
1063 public static final String REJECT = "REJECT";
1064 public static final String PENDING = "PENDING";
1066 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1067 List<ApprovalDAO.Data> approvals) {
1068 // get the approval policy for the organization
1070 // get the list of approvals with an accept status
1072 // validate the approvals against the policy
1074 // for now check if all approvals are received and return
1075 // SUCCESS/FAILURE/SKIP
1076 boolean bReject = false;
1077 boolean bPending = false;
1079 for (ApprovalDAO.Data approval : approvals) {
1080 if (approval.status.equals(REJECT)) {
1082 } else if (approval.status.equals(PENDING)) {
1087 return Result.err(Status.ERR_Policy,
1088 "Approval Polocy not conformed");
1091 return Result.err(Status.ERR_ActionNotCompleted,
1092 "Required Approvals not received");
1098 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1100 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1101 boolean all = "all".equals(cname);
1102 Result<Void> rv = null;
1104 if (all || NsDAO.TABLE.equals(cname)) {
1105 int[] seg = series(NsDAO.CACHE_SEG);
1106 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1107 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1109 if (all || PermDAO.TABLE.equals(cname)) {
1110 int[] seg = series(PermDAO.CACHE_SEG);
1111 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1112 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1114 if (all || RoleDAO.TABLE.equals(cname)) {
1115 int[] seg = series(RoleDAO.CACHE_SEG);
1116 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1117 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1119 if (all || UserRoleDAO.TABLE.equals(cname)) {
1120 int[] seg = series(UserRoleDAO.CACHE_SEG);
1121 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1122 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1124 if (all || CredDAO.TABLE.equals(cname)) {
1125 int[] seg = series(CredDAO.CACHE_SEG);
1126 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1127 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1129 if (all || CertDAO.TABLE.equals(cname)) {
1130 int[] seg = series(CertDAO.CACHE_SEG);
1131 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1132 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1136 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1141 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1143 if (NsDAO.TABLE.equals(cname)) {
1144 rv = nsDAO.invalidate(segment);
1145 } else if (PermDAO.TABLE.equals(cname)) {
1146 rv = permDAO.invalidate(segment);
1147 } else if (RoleDAO.TABLE.equals(cname)) {
1148 rv = roleDAO.invalidate(segment);
1149 } else if (UserRoleDAO.TABLE.equals(cname)) {
1150 rv = userRoleDAO.invalidate(segment);
1151 } else if (CredDAO.TABLE.equals(cname)) {
1152 rv = credDAO.invalidate(segment);
1153 } else if (CertDAO.TABLE.equals(cname)) {
1154 rv = certDAO.invalidate(segment);
1156 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1161 private int[] series(int max) {
1162 int[] series = new int[max];
1163 for (int i = 0; i < max; ++i)
1168 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1169 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1170 if (userDelegatedFor==null) {
1171 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1172 rldd.put(user, userDelegatedFor);
1174 if (userDelegatedFor.isOKhasData()) {
1175 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1176 if (curr.user.equals(approver) && curr.delegate.equals(user)
1177 && curr.expires.after(new Date())) {
1185 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1186 Boolean b = trans.get(specialLogSlot, null);
1187 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1188 if (specialLog==null) {
1191 b = specialLog.contains(user);
1192 trans.put(specialLogSlot, b);
1198 public static void logEncryptTrace(AuthzTrans trans, String data) {
1200 trans.put(transIDSlot, ti=nextTraceID());
1201 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1204 private synchronized static long nextTraceID() {
1208 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1209 if (specialLog == null) {
1210 specialLog = new HashSet<>();
1212 boolean rc = specialLog.add(id);
1214 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1219 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1220 if (specialLog==null) {
1223 boolean rv = specialLog.remove(id);
1224 if (specialLog.isEmpty()) {
1228 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1235 * Which Types can be moved
1239 public boolean canMove(NsType nsType) {
1254 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1255 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1256 if (rur.isOKhasData()) {
1257 Date now = new Date();
1258 for (UserRoleDAO.Data urdd : rur.value){
1259 if (urdd.expires.after(now)) {
1267 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1268 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1269 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1270 Date now = new Date();
1271 if (urdd.expires.after(now)) {
1278 public int countOwner(AuthzTrans trans, String ns) {
1279 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1280 Date now = new Date();
1282 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1283 if (urdd.expires.after(now)) {
1291 * Return a Unique String, (same string, if it is already unique), with only
1292 * lowercase letters, digits and the '.' character.
1296 * @throws IOException
1298 public static String toUnique(String name) throws IOException {
1299 byte[] from = name.getBytes();
1300 StringBuilder sb = new StringBuilder();
1302 for (int i=0;i<from.length;++i) {
1303 f=(byte)(from[i]); // printables;
1304 sb.append((char)((f>>4)+0x61));
1305 sb.append((char)((f&0x0F)+0x61));
1307 return sb.toString();
1310 public static String fromUnique(String name) throws IOException {
1311 byte[] from = name.getBytes();
1312 StringBuilder sb = new StringBuilder();
1314 for (int i=0;i<from.length;++i) {
1315 c = (char)((from[i]-0x61)<<4);
1316 c |= (from[++i]-0x61);
1319 return sb.toString();