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);
499 if (type.type == nsd.type) {
500 return Result.ok(nsd);
502 int dot = str.lastIndexOf('.');
505 return Result.err(Status.ERR_NsNotFound, "No Namespace for [%s]", str);
507 return deriveFirstNsForType(trans, str.substring(0, dot),type);
511 int dot = str.lastIndexOf('.');
514 return Result.err(Status.ERR_NsNotFound,"There is no valid Company Namespace for %s",str);
516 return deriveFirstNsForType(trans, str.substring(0, dot),type);
520 return Result.err(Status.ERR_NotFound, str + " does not contain type " + type.name());
523 public Result<NsSplit> deriveNsSplit(AuthzTrans trans, String child) {
524 Result<NsDAO.Data> ndd = deriveNs(trans, child);
526 NsSplit nss = new NsSplit(ndd.value, child);
528 return Result.ok(nss);
530 return Result.err(Status.ERR_NsNotFound,
531 "Cannot split [%s] into valid namespace elements",
535 return Result.err(ndd);
539 * Translate an ID into it's domain
541 * i.e. myid1234@aaf.att.com results in domain of com.att.aaf
546 public static String domain2ns(String id) {
547 int at = id.indexOf('@');
549 String[] domain = id.substring(at + 1).split("\\.");
550 StringBuilder ns = new StringBuilder(id.length());
551 boolean first = true;
552 for (int i = domain.length - 1; i >= 0; --i) {
558 ns.append(domain[i]);
560 return ns.toString();
568 * Validate Namespace of ID@Domain
570 * Namespace is reverse order of Domain.
576 public Result<NsDAO.Data> validNSOfDomain(AuthzTrans trans, String id) {
577 // Take domain, reverse order, and check on NS
579 if (id.indexOf('@')<0) { // it's already an ns, not an ID
584 if (ns.length() > 0) {
585 if (!trans.org().getDomain().equals(ns)) {
586 Result<List<NsDAO.Data>> rlnsd = nsDAO.read(trans, ns);
587 if (rlnsd.isOKhasData()) {
588 return Result.ok(rlnsd.value.get(0));
592 return Result.err(Status.ERR_NsNotFound,
593 "A Namespace is not available for %s", id);
596 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, Access access) {
597 // <ns>.access|:role:<role name>|<read|write>
598 String ns = ndd.name;
599 boolean isRoot = ns.startsWith(Define.ROOT_NS());
602 if (isGranted(trans, user, ns, ACCESS, ":ns", access.name())) {
603 return Result.ok(ndd);
608 if ((last = ns.lastIndexOf('.')) >= 0) {
609 ns = ns.substring(0, last);
613 // SAFETY - Do not allow these when NS is Root
615 // com.att.aaf.ns|:<client ns>:ns|<access>
616 // AAF-724 - Make consistent response for May User", and not take the
617 // last check... too confusing.
618 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + ndd.name + ":ns", access.name());
621 } else if (rv.status==Result.ERR_Backend) {
622 return Result.err(rv);
625 return Result.err(Status.ERR_Denied, "[%s] may not %s in NS [%s]",
626 user, access.name(), ndd.name);
630 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, RoleDAO.Data rdd, Access access) {
631 if(trans.user().equals(rdd.ns)) {
632 return Result.ok((NsDAO.Data)null);
634 Result<NsDAO.Data> rnsd = deriveNs(trans, rdd.ns);
636 return mayUser(trans, user, rnsd.value, rdd, access);
641 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user, NsDAO.Data ndd, RoleDAO.Data rdd, Access access) {
642 // 1) For "read", Is User in the Role is enough
643 if(Access.read.equals(access)) {
644 Result<List<UserRoleDAO.Data>> rurd = userRoleDAO.readUserInRole(trans, user, rdd.fullName());
645 if (rurd.isOKhasData()) {
646 return Result.ok(ndd);
650 String roleInst = ":role:" + rdd.name;
651 // <ns>.access|:role:<role name>|<read|write>
653 boolean isRoot = ns.startsWith(Define.ROOT_NS());
656 if (isGranted(trans, user, ns,ACCESS, roleInst, access.name())) {
657 return Result.ok(ndd);
662 if ((last = ns.lastIndexOf('.')) >= 0) {
663 ns = ns.substring(0, last);
667 // SAFETY - Do not allow these when NS is Root
669 // Check if Access by Global Role perm
670 // com.att.aaf.ns|:<client ns>:role:name|<access>
671 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":"
672 + rdd.ns + roleInst, access.name());
675 } else if (rnsd.status==Result.ERR_Backend) {
676 return Result.err(rnsd);
679 // Check if Access to Whole NS
680 // AAF-724 - Make consistent response for May User", and not take the
681 // last check... too confusing.
682 Result<org.onap.aaf.auth.dao.cass.NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd,
683 ":" + rdd.ns + ":ns", access.name());
686 } else if (rnsd.status==Result.ERR_Backend) {
687 return Result.err(rnsd);
690 return Result.err(Status.ERR_Denied, "[%s] may not %s Role [%s]",
691 user, access.name(), rdd.fullName());
694 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,PermDAO.Data pdd, Access access) {
695 if(pdd.ns.indexOf('@')>-1) {
696 if(user.equals(pdd.ns) || isGranted(trans,user,Define.ROOT_NS(),"access",pdd.instance,READ)) {
697 NsDAO.Data ndd = new NsDAO.Data();
699 ndd.type = NsDAO.USER;
701 return Result.ok(ndd);
703 return Result.err(Result.ERR_Security,"Only a User may modify User");
706 Result<NsDAO.Data> rnsd = deriveNs(trans, pdd.ns);
708 return mayUser(trans, user, rnsd.value, pdd, access);
713 public Result<NsDAO.Data> mayUser(AuthzTrans trans, String user,NsDAO.Data ndd, PermDAO.Data pdd, Access access) {
714 // Most common occurrence... if granted Permission
715 if (isGranted(trans, user, pdd.ns, pdd.type, pdd.instance, pdd.action)) {
716 return Result.ok(ndd);
719 String permInst = ":perm:" + pdd.type + ':' + pdd.instance + ':' + pdd.action;
720 // <ns>.access|:role:<role name>|<read|write>
721 String ns = ndd.name;
722 boolean isRoot = ns.startsWith(Define.ROOT_NS());
725 if (isGranted(trans, user, ns, ACCESS, permInst, access.name())) {
726 return Result.ok(ndd);
731 if ((last = ns.lastIndexOf('.')) >= 0) {
732 ns = ns.substring(0, last);
736 // SAFETY - Do not allow these when NS is Root
738 // Check if Access by NS perm
739 // com.att.aaf.ns|:<client ns>:role:name|<access>
740 Result<NsDAO.Data> rnsd = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + permInst, access.name());
743 } else if (rnsd.status==Result.ERR_Backend) {
744 return Result.err(rnsd);
747 // Check if Access to Whole NS
748 // AAF-724 - Make consistent response for May User", and not take the
749 // last check... too confusing.
750 Result<NsDAO.Data> rv = mayUserVirtueOfNS(trans, user, ndd, ":" + pdd.ns + ":ns", access.name());
755 return Result.err(Status.ERR_Denied,
756 "[%s] may not %s Perm [%s|%s|%s]", user, access.name(),
757 pdd.fullType(), pdd.instance, pdd.action);
760 public Result<Void> mayUser(AuthzTrans trans, DelegateDAO.Data dd, Access access) {
762 Result<NsDAO.Data> rnsd = deriveNs(trans, domain2ns(trans.user()));
763 if (rnsd.isOKhasData() && mayUserVirtueOfNS(trans,trans.user(),rnsd.value, ":" + rnsd.value.name + ":ns", access.name()).isOK()) {
766 boolean isUser = trans.user().equals(dd.user);
767 boolean isDelegate = dd.delegate != null
768 && (dd.user.equals(dd.delegate) || trans.user().equals(
770 Organization org = trans.org();
773 if (org.getIdentity(trans, dd.user) == null) {
774 return Result.err(Status.ERR_UserNotFound,
775 "[%s] is not a user in the company database.",
778 if (!dd.user.equals(dd.delegate) && org.getIdentity(trans, dd.delegate) == null) {
779 return Result.err(Status.ERR_UserNotFound,
780 "[%s] is not a user in the company database.",
783 if (!trans.requested(REQD_TYPE.force) && dd.user != null && dd.user.equals(dd.delegate)) {
784 return Result.err(Status.ERR_BadData,
785 "[%s] cannot be a delegate for self", dd.user);
787 if (!isUser && !isGranted(trans, trans.user(), ROOT_NS,DELG,
788 org.getDomain(), Question.CREATE)) {
789 return Result.err(Status.ERR_Denied,
790 "[%s] may not create a delegate for [%s]",
791 trans.user(), dd.user);
796 if (!isUser && !isDelegate &&
797 !isGranted(trans, trans.user(), ROOT_NS,DELG,org.getDomain(), access.name())) {
798 return Result.err(Status.ERR_Denied,
799 "[%s] may not %s delegates for [%s]", trans.user(),
800 access.name(), dd.user);
804 return Result.err(Status.ERR_BadData,"Unknown Access type [%s]", access.name());
806 } catch (Exception e) {
807 return Result.err(e);
813 * Check (recursively, if necessary), if able to do something based on NS
815 private Result<NsDAO.Data> mayUserVirtueOfNS(AuthzTrans trans, String user, NsDAO.Data nsd, String ns_and_type, String access) {
816 String ns = nsd.name;
818 // If an ADMIN of the Namespace, then allow
820 Result<List<UserRoleDAO.Data>> rurd;
821 if ((rurd = userRoleDAO.readUserInRole(trans, user, ns+DOT_ADMIN)).isOKhasData()) {
822 return Result.ok(nsd);
823 } else if (rurd.status==Result.ERR_Backend) {
824 return Result.err(rurd);
827 // If Specially granted Global Permission
828 if (isGranted(trans, user, ROOT_NS,NS, ns_and_type, access)) {
829 return Result.ok(nsd);
834 int dot = ns.length();
835 if ((dot = ns.lastIndexOf('.', dot - 1)) >= 0) {
836 Result<NsDAO.Data> rnsd = deriveNs(trans, ns.substring(0, dot));
838 rnsd = mayUserVirtueOfNS(trans, user, rnsd.value, ns_and_type,access);
839 } else if (rnsd.status==Result.ERR_Backend) {
840 return Result.err(rnsd);
843 return Result.ok(nsd);
844 } else if (rnsd.status==Result.ERR_Backend) {
845 return Result.err(rnsd);
848 return Result.err(Status.ERR_Denied, "%s may not %s %s", user, access,
856 * Important function - Check internal Permission Schemes for Permission to
865 public boolean isGranted(AuthzTrans trans, String user, String ns, String type,String instance, String action) {
866 Result<List<PermDAO.Data>> perms = getPermsByUser(trans, user, false);
868 for (PermDAO.Data pd : perms.value) {
869 if (ns.equals(pd.ns)) {
870 if (type.equals(pd.type)) {
871 if (PermEval.evalInstance(pd.instance, instance)) {
872 if (PermEval.evalAction(pd.action, action)) { // don't return action here, might miss other action
883 public Result<Date> doesUserCredMatch(AuthzTrans trans, String user, byte[] cred) throws DAOException {
884 Result<List<CredDAO.Data>> result;
885 TimeTaken tt = trans.start("Read DB Cred", Env.REMOTE);
887 result = credDAO.readIDBAth(trans, user);
892 Result<Date> rv = null;
894 if (result.isEmpty()) {
895 rv = Result.err(Status.ERR_UserNotFound, user);
896 if (willSpecialLog(trans,user)) {
897 trans.audit().log("Special DEBUG:", user, " does not exist in DB");
900 Date now = new Date();
901 // Bug noticed 6/22. Sorting on the result can cause Concurrency Issues.
902 List<CredDAO.Data> cddl;
903 if (result.value.size() > 1) {
904 cddl = new ArrayList<>(result.value.size());
905 for (CredDAO.Data old : result.value) {
906 if (old.type==CredDAO.BASIC_AUTH || old.type==CredDAO.BASIC_AUTH_SHA256) {
911 Collections.sort(cddl, (a, b) -> b.expires.compareTo(a.expires));
918 StringBuilder debug = willSpecialLog(trans,user)?new StringBuilder():null;
919 for (CredDAO.Data cdd : cddl) {
920 if (!cdd.id.equals(user)) {
921 trans.error().log("doesUserCredMatch DB call does not match for user: " + user);
923 if (cdd.expires.after(now)) {
924 byte[] dbcred = cdd.cred.array();
928 case CredDAO.BASIC_AUTH:
929 byte[] md5=Hash.hashMD5(cred);
930 if (Hash.compareTo(md5,dbcred)==0) {
931 checkLessThanDays(trans,cldays,now,cdd);
932 trans.setTag(cdd.tag);
933 return Result.ok(cdd.expires);
934 } else if (debug!=null) {
938 case CredDAO.BASIC_AUTH_SHA256:
939 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.length);
940 bb.putInt(cdd.other);
942 byte[] hash = Hash.hashSHA256(bb.array());
944 if (Hash.compareTo(hash,dbcred)==0) {
945 checkLessThanDays(trans,cldays,now,cdd);
946 trans.setTag(cdd.tag);
947 return Result.ok(cdd.expires);
948 } else if (debug!=null) {
953 trans.error().log("Unknown Credential Type %s for %s, %s",Integer.toString(cdd.type),cdd.id, Chrono.dateTime(cdd.expires));
955 } catch (NoSuchAlgorithmException e) {
956 trans.error().log(e);
959 if (expired==null || expired.before(cdd.expires)) {
960 expired = cdd.expires;
961 trans.setTag(cdd.tag);
967 // Note: this is only returned if there are no good Credentials
968 rv = Result.err(Status.ERR_Security,
969 "Credentials expired %s",Chrono.utcStamp(expired));
971 if (debug==null && alwaysSpecial) {
972 debug = new StringBuilder();
975 debug.append(trans.env().encryptor().encrypt(new String(cred)));
976 rv = Result.err(Status.ERR_Security,String.format("invalid password - %s",debug.toString()));
981 return Result.err(result);
983 return rv == null ? Result.err(Status.ERR_Security, "Wrong credential") : rv;
987 private void load(StringBuilder debug, Data cdd) {
988 debug.append("\nDB Entry: user=");
989 debug.append(cdd.id);
990 debug.append(",type=");
991 debug.append(cdd.type);
992 debug.append(",expires=");
993 debug.append(Chrono.dateTime(cdd.expires));
994 debug.append(",tag=");
995 debug.append(cdd.tag);
1000 private void checkLessThanDays(AuthzTrans trans, int days, Date now, Data cdd) {
1001 long close = now.getTime() + (days * 86400000);
1002 long cexp=cdd.expires.getTime();
1004 int daysLeft = days-(int)((close-cexp)/86400000);
1005 trans.audit().printf("user=%s,ip=%s,expires=%s,days=%d,tag=%s,msg=\"Password expires in less than %d day%s\"",
1006 cdd.id,trans.ip(),Chrono.dateOnlyStamp(cdd.expires),daysLeft, cdd.tag,
1007 daysLeft,daysLeft==1?"":"s");
1012 public Result<CredDAO.Data> userCredSetup(AuthzTrans trans, CredDAO.Data cred) {
1013 if (cred.type==CredDAO.RAW) {
1014 TimeTaken tt = trans.start("Hash Cred", Env.SUB);
1016 cred.type = CredDAO.BASIC_AUTH_SHA256;
1017 cred.other = random.nextInt();
1018 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + cred.cred.capacity());
1019 bb.putInt(cred.other);
1021 byte[] hash = Hash.hashSHA256(bb.array());
1022 cred.cred = ByteBuffer.wrap(hash);
1023 return Result.ok(cred);
1024 } catch (NoSuchAlgorithmException e) {
1025 return Result.err(Status.ERR_General,e.getLocalizedMessage());
1030 } else if (cred.type==CredDAO.FQI) {
1032 return Result.ok(cred);
1034 return Result.err(Status.ERR_Security,"invalid/unreadable credential");
1037 public Result<Boolean> userCredCheck(AuthzTrans trans, CredDAO.Data orig, final byte[] raw) {
1039 TimeTaken tt = trans.start("CheckCred Cred", Env.SUB);
1042 case CredDAO.BASIC_AUTH_SHA256:
1043 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + raw.length);
1044 bb.putInt(orig.other);
1046 rv = Result.ok(Hash.compareTo(orig.cred.array(),Hash.hashSHA256(bb.array()))==0);
1047 case CredDAO.BASIC_AUTH:
1048 rv= Result.ok( Hash.compareTo(orig.cred.array(), Hash.hashMD5(raw))==0);
1051 rv = Result.ok(false);
1053 } catch (NoSuchAlgorithmException e) {
1054 rv = Result.err(Status.ERR_General,e.getLocalizedMessage());
1061 public static final String APPROVED = "APPROVE";
1062 public static final String REJECT = "REJECT";
1063 public static final String PENDING = "PENDING";
1065 public Result<Void> canAddUser(AuthzTrans trans, UserRoleDAO.Data data,
1066 List<ApprovalDAO.Data> approvals) {
1067 // get the approval policy for the organization
1069 // get the list of approvals with an accept status
1071 // validate the approvals against the policy
1073 // for now check if all approvals are received and return
1074 // SUCCESS/FAILURE/SKIP
1075 boolean bReject = false;
1076 boolean bPending = false;
1078 for (ApprovalDAO.Data approval : approvals) {
1079 if (approval.status.equals(REJECT)) {
1081 } else if (approval.status.equals(PENDING)) {
1086 return Result.err(Status.ERR_Policy,
1087 "Approval Polocy not conformed");
1090 return Result.err(Status.ERR_ActionNotCompleted,
1091 "Required Approvals not received");
1097 private static final String NO_CACHE_NAME = "No Cache Data named %s";
1099 public Result<Void> clearCache(AuthzTrans trans, String cname) {
1100 boolean all = "all".equals(cname);
1101 Result<Void> rv = null;
1103 if (all || NsDAO.TABLE.equals(cname)) {
1104 int[] seg = series(NsDAO.CACHE_SEG);
1105 for (int i: seg) {cacheClear(trans, NsDAO.TABLE,i);}
1106 rv = cacheInfoDAO.touch(trans, NsDAO.TABLE, seg);
1108 if (all || PermDAO.TABLE.equals(cname)) {
1109 int[] seg = series(PermDAO.CACHE_SEG);
1110 for (int i: seg) {cacheClear(trans, PermDAO.TABLE,i);}
1111 rv = cacheInfoDAO.touch(trans, PermDAO.TABLE,seg);
1113 if (all || RoleDAO.TABLE.equals(cname)) {
1114 int[] seg = series(RoleDAO.CACHE_SEG);
1115 for (int i: seg) {cacheClear(trans, RoleDAO.TABLE,i);}
1116 rv = cacheInfoDAO.touch(trans, RoleDAO.TABLE,seg);
1118 if (all || UserRoleDAO.TABLE.equals(cname)) {
1119 int[] seg = series(UserRoleDAO.CACHE_SEG);
1120 for (int i: seg) {cacheClear(trans, UserRoleDAO.TABLE,i);}
1121 rv = cacheInfoDAO.touch(trans, UserRoleDAO.TABLE,seg);
1123 if (all || CredDAO.TABLE.equals(cname)) {
1124 int[] seg = series(CredDAO.CACHE_SEG);
1125 for (int i: seg) {cacheClear(trans, CredDAO.TABLE,i);}
1126 rv = cacheInfoDAO.touch(trans, CredDAO.TABLE,seg);
1128 if (all || CertDAO.TABLE.equals(cname)) {
1129 int[] seg = series(CertDAO.CACHE_SEG);
1130 for (int i: seg) {cacheClear(trans, CertDAO.TABLE,i);}
1131 rv = cacheInfoDAO.touch(trans, CertDAO.TABLE,seg);
1135 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1140 public Result<Void> cacheClear(AuthzTrans trans, String cname,Integer segment) {
1142 if (NsDAO.TABLE.equals(cname)) {
1143 rv = nsDAO.invalidate(segment);
1144 } else if (PermDAO.TABLE.equals(cname)) {
1145 rv = permDAO.invalidate(segment);
1146 } else if (RoleDAO.TABLE.equals(cname)) {
1147 rv = roleDAO.invalidate(segment);
1148 } else if (UserRoleDAO.TABLE.equals(cname)) {
1149 rv = userRoleDAO.invalidate(segment);
1150 } else if (CredDAO.TABLE.equals(cname)) {
1151 rv = credDAO.invalidate(segment);
1152 } else if (CertDAO.TABLE.equals(cname)) {
1153 rv = certDAO.invalidate(segment);
1155 rv = Result.err(Status.ERR_BadData, NO_CACHE_NAME, cname);
1160 private int[] series(int max) {
1161 int[] series = new int[max];
1162 for (int i = 0; i < max; ++i)
1167 public boolean isDelegated(AuthzTrans trans, String user, String approver, Map<String,Result<List<DelegateDAO.Data>>> rldd ) {
1168 Result<List<DelegateDAO.Data>> userDelegatedFor = rldd.get(user);
1169 if (userDelegatedFor==null) {
1170 userDelegatedFor=delegateDAO.readByDelegate(trans, user);
1171 rldd.put(user, userDelegatedFor);
1173 if (userDelegatedFor.isOKhasData()) {
1174 for (DelegateDAO.Data curr : userDelegatedFor.value) {
1175 if (curr.user.equals(approver) && curr.delegate.equals(user)
1176 && curr.expires.after(new Date())) {
1184 public static boolean willSpecialLog(AuthzTrans trans, String user) {
1185 Boolean b = trans.get(specialLogSlot, null);
1186 if (b==null) { // we haven't evaluated in this trans for Special Log yet
1187 if (specialLog==null) {
1190 b = specialLog.contains(user);
1191 trans.put(specialLogSlot, b);
1197 public static void logEncryptTrace(AuthzTrans trans, String data) {
1199 trans.put(transIDSlot, ti=nextTraceID());
1200 trans.trace().log("id="+Long.toHexString(ti)+",data=\""+trans.env().encryptor().encrypt(data)+'"');
1203 private synchronized static long nextTraceID() {
1207 public static synchronized boolean specialLogOn(AuthzTrans trans, String id) {
1208 if (specialLog == null) {
1209 specialLog = new HashSet<>();
1211 boolean rc = specialLog.add(id);
1213 trans.trace().printf("Trace on for %s requested by %s",id,trans.user());
1218 public static synchronized boolean specialLogOff(AuthzTrans trans, String id) {
1219 if (specialLog==null) {
1222 boolean rv = specialLog.remove(id);
1223 if (specialLog.isEmpty()) {
1227 trans.trace().printf("Trace off for %s requested by %s",id,trans.user());
1234 * Which Types can be moved
1238 public boolean canMove(NsType nsType) {
1253 public boolean isAdmin(AuthzTrans trans, String user, String ns) {
1254 Result<List<UserRoleDAO.Data>> rur = userRoleDAO.read(trans, user,ns+DOT_ADMIN);
1255 if (rur.isOKhasData()) {
1256 Date now = new Date();
1257 for (UserRoleDAO.Data urdd : rur.value){
1258 if (urdd.expires.after(now)) {
1266 public boolean isOwner(AuthzTrans trans, String user, String ns) {
1267 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().read(trans, user,ns+DOT_OWNER);
1268 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1269 Date now = new Date();
1270 if (urdd.expires.after(now)) {
1277 public int countOwner(AuthzTrans trans, String ns) {
1278 Result<List<UserRoleDAO.Data>> rur = userRoleDAO().readByRole(trans,ns+DOT_OWNER);
1279 Date now = new Date();
1281 if (rur.isOKhasData()) {for (UserRoleDAO.Data urdd : rur.value){
1282 if (urdd.expires.after(now)) {
1290 * Return a Unique String, (same string, if it is already unique), with only
1291 * lowercase letters, digits and the '.' character.
1295 * @throws IOException
1297 public static String toUnique(String name) throws IOException {
1298 byte[] from = name.getBytes();
1299 StringBuilder sb = new StringBuilder();
1301 for (int i=0;i<from.length;++i) {
1302 f=(byte)(from[i]); // printables;
1303 sb.append((char)((f>>4)+0x61));
1304 sb.append((char)((f&0x0F)+0x61));
1306 return sb.toString();
1309 public static String fromUnique(String name) throws IOException {
1310 byte[] from = name.getBytes();
1311 StringBuilder sb = new StringBuilder();
1313 for (int i=0;i<from.length;++i) {
1314 c = (char)((from[i]-0x61)<<4);
1315 c |= (from[++i]-0x61);
1318 return sb.toString();