X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=auth%2Fauth-cass%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Faaf%2Fauth%2Fdao%2Fhl%2FFunction.java;fp=auth%2Fauth-cass%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Faaf%2Fauth%2Fdao%2Fhl%2FFunction.java;h=f3aae2ec8b7b3c55b47041605153faebe2223b44;hb=4b5a7d721d994a49057e9bfb403c7bff1b376660;hp=8529ce87e723bb42eaf6bfb132778b71b4cb726c;hpb=824dc7b5fc0e1ccdf7f460479aff344727f0f01e;p=aaf%2Fauthz.git diff --git a/auth/auth-cass/src/main/java/org/onap/aaf/auth/dao/hl/Function.java b/auth/auth-cass/src/main/java/org/onap/aaf/auth/dao/hl/Function.java index 8529ce87..f3aae2ec 100644 --- a/auth/auth-cass/src/main/java/org/onap/aaf/auth/dao/hl/Function.java +++ b/auth/auth-cass/src/main/java/org/onap/aaf/auth/dao/hl/Function.java @@ -60,1733 +60,1733 @@ import org.onap.aaf.auth.org.OrganizationException; public class Function { - private static final String CANNOT_BE_THE_OWNER_OF_A_NAMESPACE = "%s(%s) cannot be the owner of the namespace '%s'. Owners %s."; - - public enum FUTURE_OP { - C("Create"),U("Update"),D("Delete"),G("Grant"),UG("UnGrant"),A("Approval"); - - private String desc; - - private FUTURE_OP(String desc) { - this.desc = desc; - } - - public String desc() { - return desc; - } - - /** - * Same as valueOf(), but passes back null instead of throwing Exception - * @param value - * @return - */ - public static FUTURE_OP toFO(String value) { - if(value!=null) { - for(FUTURE_OP fo : values()) { - if(fo.name().equals(value)){ - return fo; - } - } - } - return null; - } - } - - public enum OP_STATUS { - E("Executed"),D("Denied"),P("Pending"),L("Lapsed"); - - private String desc; - public final static Result RE = Result.ok(OP_STATUS.E); - public final static Result RD = Result.ok(OP_STATUS.D); - public final static Result RP = Result.ok(OP_STATUS.P); - public final static Result RL = Result.ok(OP_STATUS.L); - - private OP_STATUS(String desc) { - this.desc = desc; - } - - public String desc() { - return desc; - } - - } - - public static final String FOP_CRED = "cred"; - public static final String FOP_DELEGATE = "delegate"; - public static final String FOP_NS = "ns"; - public static final String FOP_PERM = "perm"; - public static final String FOP_ROLE = "role"; - public static final String FOP_USER_ROLE = "user_role"; - private static final List NO_ADDL_APPROVE = new ArrayList<>(); - private static final String ROOT_NS = Define.ROOT_NS(); - // First Action should ALWAYS be "write", see "CreateRole" - public final Question q; - - public Function(AuthzTrans trans, Question question) { - q = question; - } - - private class ErrBuilder { - private StringBuilder sb; - private List ao; - - public void log(Result result) { - if (result.notOK()) { - if (sb == null) { - sb = new StringBuilder(); - ao = new ArrayList<>(); - } - sb.append(result.details); - sb.append('\n'); - for (String s : result.variables) { - ao.add(s); - } - } - } - - public String[] vars() { - String[] rv = new String[ao.size()]; - ao.toArray(rv); - return rv; - } - - public boolean hasErr() { - return sb != null; - } - - @Override - public String toString() { - return sb == null ? "" : String.format(sb.toString(), ao); - } - } - - /** - * createNS - * - * Create Namespace - * - * @param trans - * @param org - * @param ns - * @param user - * @return - * @throws DAOException - * - * To create an NS, you need to: 1) validate permission to - * modify parent NS 2) Does NS exist already? 3) Create NS with - * a) "user" as owner. NOTE: Per 10-15 request for AAF 1.0 4) - * Loop through Roles with Parent NS, and map any that start - * with this NS into this one 5) Loop through Perms with Parent - * NS, and map any that start with this NS into this one - */ - public Result createNS(AuthzTrans trans, Namespace namespace, boolean fromApproval) { - Result rq; -// if (namespace.name.endsWith(Question.DOT_ADMIN) -// || namespace.name.endsWith(Question.DOT_OWNER)) { -// return Result.err(Status.ERR_BadData, -// "'admin' and 'owner' are reserved names in AAF"); -// } - - try { - for (String u : namespace.owner) { - Organization org = trans.org(); - Identity orgUser = org.getIdentity(trans, u); - String reason; - if (orgUser == null) { - return Result.err(Status.ERR_Policy,"%s is not a valid user at %s",u,org.getName()); - } else if((reason=orgUser.mayOwn())!=null) { - if (org.isTestEnv()) { - String reason2; - if((reason2=org.validate(trans, Policy.AS_RESPONSIBLE,new CassExecutor(trans, this), u))!=null) { // can masquerade as responsible - trans.debug().log(reason2); - return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name,reason); - } - // a null means ok - } else { - if(orgUser.isFound()) { - return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name, reason); - } else { - return Result.err(Status.ERR_Policy,u + " is an invalid Identity"); - } - } - } - } - } catch (Exception e) { - trans.error().log(e, - "Could not contact Organization for User Validation"); - } - - String user = trans.user(); - // 1) May Change Parent? - int idx = namespace.name.lastIndexOf('.'); - String parent; - if (idx < 0) { - if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "create")) { - return Result.err(Result.ERR_Security, - "%s may not create Root Namespaces", user); - } - parent = null; - fromApproval = true; - } else { - parent = namespace.name.substring(0, idx); // get Parent String - } - - Result rparent = q.deriveNs(trans, parent); - if (rparent.notOK()) { - return Result.err(rparent); - } - if (!fromApproval) { - rparent = q.mayUser(trans, user, rparent.value, Access.write); - if (rparent.notOK()) { - return Result.err(rparent); - } - } - parent = namespace.parent = rparent.value.name; // Correct Namespace from real data - - // 2) Does requested NS exist - if (q.nsDAO.read(trans, namespace.name).isOKhasData()) { - return Result.err(Status.ERR_ConflictAlreadyExists, - "Target Namespace already exists"); - } - - // Someone must be responsible. - if (namespace.owner == null || namespace.owner.isEmpty()) { - return Result - .err(Status.ERR_Policy, - "Namespaces must be assigned at least one responsible party"); - } - - // 3) Create NS - Date now = new Date(); - - Result r; - // 3a) Admin - - try { - // Originally, added the enterer as Admin, but that's not necessary, - // or helpful for Operations folks.. - // Admins can be empty, because they can be changed by lower level - // NSs - // if(ns.admin(false).isEmpty()) { - // ns.admin(true).add(user); - // } - if (namespace.admin != null) { - for (String u : namespace.admin) { - if ((r = checkValidID(trans, now, u)).notOK()) { - return r; - } - } - } - - // 3b) Responsible - Organization org = trans.org(); - for (String u : namespace.owner) { - Identity orgUser = org.getIdentity(trans, u); - if (orgUser == null) { - return Result - .err(Status.ERR_BadData, - "NS must be created with an %s approved Responsible Party", - org.getName()); - } - } - } catch (Exception e) { - return Result.err(Status.ERR_UserNotFound, e.getMessage()); - } - - // VALIDATIONS done... Add NS - if ((rq = q.nsDAO.create(trans, namespace.data())).notOK()) { - return Result.err(rq); - } - - // Since Namespace is now created, we need to grab all subsequent errors - ErrBuilder eb = new ErrBuilder(); - - // Add UserRole(s) - UserRoleDAO.Data urdd = new UserRoleDAO.Data(); - urdd.expires = trans.org().expiration(null, Expiration.UserInRole).getTime(); - urdd.role(namespace.name, Question.ADMIN); - for (String admin : namespace.admin) { - urdd.user = admin; - eb.log(q.userRoleDAO.create(trans, urdd)); - } - urdd.role(namespace.name,Question.OWNER); - for (String owner : namespace.owner) { - urdd.user = owner; - eb.log(q.userRoleDAO.create(trans, urdd)); - } - - addNSAdminRolesPerms(trans, eb, namespace.name); - - addNSOwnerRolesPerms(trans, eb, namespace.name); - - if (parent != null) { - // Build up with any errors - - String targetNs = rparent.value.name; // Get the Parent Namespace, - // not target - String targetName = namespace.name.substring(targetNs.length() + 1); // Remove the Parent Namespace from the - // Target + a dot, and you'll get the name - int targetNameDot = targetName.length() + 1; - - // 4) Change any roles with children matching this NS, and - Result> rrdc = q.roleDAO.readChildren(trans, targetNs, targetName); - if (rrdc.isOKhasData()) { - for (RoleDAO.Data rdd : rrdc.value) { - // Remove old Role from Perms, save them off - List lpdd = new ArrayList<>(); - for(String p : rdd.perms(false)) { - Result rpdd = PermDAO.Data.decode(trans,q,p); - if(rpdd.isOKhasData()) { - PermDAO.Data pdd = rpdd.value; - lpdd.add(pdd); - q.permDAO.delRole(trans, pdd, rdd); - } else{ - trans.error().log(rpdd.errorString()); - } - } - - // Save off Old keys - String delP1 = rdd.ns; - String delP2 = rdd.name; - - // Write in new key - rdd.ns = namespace.name; - rdd.name = (delP2.length() > targetNameDot) ? delP2 - .substring(targetNameDot) : ""; - - // Need to use non-cached, because switching namespaces, not - // "create" per se - if ((rq = q.roleDAO.create(trans, rdd)).isOK()) { - // Put Role back into Perm, with correct info - for(PermDAO.Data pdd : lpdd) { - q.permDAO.addRole(trans, pdd, rdd); - } - // Change data for User Roles - Result> rurd = q.userRoleDAO.readByRole(trans, rdd.fullName()); - if(rurd.isOKhasData()) { - for(UserRoleDAO.Data urd : rurd.value) { - urd.ns = rdd.ns; - urd.rname = rdd.name; - q.userRoleDAO.update(trans, urd); - } - } - // Now delete old one - rdd.ns = delP1; - rdd.name = delP2; - if ((rq = q.roleDAO.delete(trans, rdd, false)).notOK()) { - eb.log(rq); - } - } else { - eb.log(rq); - } - } - } - - // 4) Change any Permissions with children matching this NS, and - Result> rpdc = q.permDAO.readChildren(trans,targetNs, targetName); - if (rpdc.isOKhasData()) { - for (PermDAO.Data pdd : rpdc.value) { - // Remove old Perm from Roles, save them off - List lrdd = new ArrayList<>(); - - for(String rl : pdd.roles(false)) { - Result rrdd = RoleDAO.Data.decode(trans,q,rl); - if(rrdd.isOKhasData()) { - RoleDAO.Data rdd = rrdd.value; - lrdd.add(rdd); - q.roleDAO.delPerm(trans, rdd, pdd); - } else{ - trans.error().log(rrdd.errorString()); - } - } - - // Save off Old keys - String delP1 = pdd.ns; - String delP2 = pdd.type; - pdd.ns = namespace.name; - pdd.type = (delP2.length() > targetNameDot) ? delP2 - .substring(targetNameDot) : ""; - if ((rq = q.permDAO.create(trans, pdd)).isOK()) { - // Put Role back into Perm, with correct info - for(RoleDAO.Data rdd : lrdd) { - q.roleDAO.addPerm(trans, rdd, pdd); - } - - pdd.ns = delP1; - pdd.type = delP2; - if ((rq = q.permDAO.delete(trans, pdd, false)).notOK()) { - eb.log(rq); - // } else { - // Need to invalidate directly, because we're - // switching places in NS, not normal cache behavior - // q.permDAO.invalidate(trans,pdd); - } - } else { - eb.log(rq); - } - } - } - if (eb.hasErr()) { - return Result.err(Status.ERR_ActionNotCompleted,eb.sb.toString(), eb.vars()); - } - } - return Result.ok(); - } - - private void addNSAdminRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) { - // Admin Role/Perm - RoleDAO.Data rd = new RoleDAO.Data(); - rd.ns = ns; - rd.name = "admin"; - rd.description = "AAF Namespace Administrators"; - - PermDAO.Data pd = new PermDAO.Data(); - pd.ns = ns; - pd.type = "access"; - pd.instance = Question.ASTERIX; - pd.action = Question.ASTERIX; - pd.description = "AAF Namespace Write Access"; - - rd.perms = new HashSet<>(); - rd.perms.add(pd.encode()); - eb.log(q.roleDAO.create(trans, rd)); - - pd.roles = new HashSet<>(); - pd.roles.add(rd.encode()); - eb.log(q.permDAO.create(trans, pd)); - } - - private void addNSOwnerRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) { - RoleDAO.Data rd = new RoleDAO.Data(); - rd.ns = ns; - rd.name = "owner"; - rd.description = "AAF Namespace Owners"; - - PermDAO.Data pd = new PermDAO.Data(); - pd.ns = ns; - pd.type = "access"; - pd.instance = Question.ASTERIX; - pd.action = Question.READ; - pd.description = "AAF Namespace Read Access"; - - rd.perms = new HashSet<>(); - rd.perms.add(pd.encode()); - eb.log(q.roleDAO.create(trans, rd)); - - pd.roles = new HashSet<>(); - pd.roles.add(rd.encode()); - eb.log(q.permDAO.create(trans, pd)); - } - - /** - * deleteNS - * - * Delete Namespace - * - * @param trans - * @param org - * @param ns - * @param force - * @param user - * @return - * @throws DAOException - * - * - * To delete an NS, you need to: 1) validate permission to - * modify this NS 2) Find all Roles with this NS, and 2a) if - * Force, delete them, else modify to Parent NS 3) Find all - * Perms with this NS, and modify to Parent NS 3a) if Force, - * delete them, else modify to Parent NS 4) Find all IDs - * associated to this NS, and deny if exists. 5) Remove NS - */ - public Result deleteNS(AuthzTrans trans, String ns) { - boolean force = trans.requested(REQD_TYPE.force); - boolean move = trans.requested(REQD_TYPE.move); - // 1) Validate - Result> nsl; - if ((nsl = q.nsDAO.read(trans, ns)).notOKorIsEmpty()) { - return Result.err(Status.ERR_NsNotFound, "%s does not exist", ns); - } - NsDAO.Data nsd = nsl.value.get(0); - NsType nt; - if (move && !q.canMove(nt = NsType.fromType(nsd.type))) { - return Result.err(Status.ERR_Denied, "Namespace Force=move not permitted for Type %s",nt.name()); - } - - Result dnr = q.mayUser(trans, trans.user(), nsd, Access.write); - if (dnr.status != Status.OK) { - return Result.err(dnr); - } - - // 2) Find Parent - String user = trans.user(); - int idx = ns.lastIndexOf('.'); - NsDAO.Data parent; - if (idx < 0) { - if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "delete")) { - return Result.err(Result.ERR_Security, - "%s may not delete Root Namespaces", user); - } - parent = null; - } else { - Result rlparent = q.deriveNs(trans, ns.substring(0, idx)); - if (rlparent.notOKorIsEmpty()) { - return Result.err(rlparent); - } - parent = rlparent.value; - } - - // Build up with any errors - // If sb != null below is an indication of error - StringBuilder sb = null; - ErrBuilder er = new ErrBuilder(); - - // 2a) Deny if any IDs on Namespace - Result> creds = q.credDAO.readNS(trans, ns); - if (creds.isOKhasData()) { - if (force || move) { - for (CredDAO.Data cd : creds.value) { - er.log(q.credDAO.delete(trans, cd, false)); - // Since we're deleting all the creds, we should delete all - // the user Roles for that Cred - Result> rlurd = q.userRoleDAO - .readByUser(trans, cd.id); - if (rlurd.isOK()) { - for (UserRoleDAO.Data data : rlurd.value) { - q.userRoleDAO.delete(trans, data, false); - } - } - - } - } else { - // first possible StringBuilder Create. - sb = new StringBuilder(); - sb.append('['); - sb.append(ns); - sb.append("] contains users"); - } - } - - // 2b) Find (or delete if forced flag is set) dependencies - // First, find if NS Perms are the only ones - Result> rpdc = q.permDAO.readNS(trans, ns); - if (rpdc.isOKhasData()) { - // Since there are now NS perms, we have to count NON-NS perms. - // FYI, if we delete them now, and the NS is not deleted, it is in - // an inconsistent state. - boolean nonaccess = false; - for (PermDAO.Data pdd : rpdc.value) { - if (!"access".equals(pdd.type)) { - nonaccess = true; - break; - } - } - if (nonaccess && !force && !move) { - if (sb == null) { - sb = new StringBuilder(); - sb.append('['); - sb.append(ns); - sb.append("] contains "); - } else { - sb.append(", "); - } - sb.append("permissions"); - } - } - - Result> rrdc = q.roleDAO.readNS(trans, ns); - if (rrdc.isOKhasData()) { - // Since there are now NS roles, we have to count NON-NS roles. - // FYI, if we delete th)em now, and the NS is not deleted, it is in - // an inconsistent state. - int count = rrdc.value.size(); - for (RoleDAO.Data rdd : rrdc.value) { - if ("admin".equals(rdd.name) || "owner".equals(rdd.name)) { - --count; - } - } - if (count > 0 && !force && !move) { - if (sb == null) { - sb = new StringBuilder(); - sb.append('['); - sb.append(ns); - sb.append("] contains "); - } else { - sb.append(", "); - } - sb.append("roles"); - } - } - - // 2c) Deny if dependencies exist that would be moved to root level - // parent is root level parent here. Need to find closest parent ns that - // exists - if (sb != null) { - if (!force && !move) { - sb.append(".\n Delete dependencies and try again. Note: using \"force=true\" will delete all. \"force=move\" will delete Creds, but move Roles and Perms to parent."); - return Result.err(Status.ERR_DependencyExists, sb.toString()); - } - - if (move && (parent == null || parent.type == NsType.COMPANY.type)) { - return Result - .err(Status.ERR_DependencyExists, - "Cannot move users, roles or permissions to [%s].\nDelete dependencies and try again", - parent.name); - } - } else if (move && parent != null) { - sb = new StringBuilder(); - // 3) Change any roles with children matching this NS, and - moveRoles(trans, parent, sb, rrdc); - // 4) Change any Perms with children matching this NS, and - movePerms(trans, parent, sb, rpdc); - } - - if (sb != null && sb.length() > 0) { - return Result.err(Status.ERR_DependencyExists, sb.toString()); - } - - if (er.hasErr()) { - if (trans.debug().isLoggable()) { - trans.debug().log(er.toString()); - } - return Result.err(Status.ERR_DependencyExists, - "Namespace members cannot be deleted for %s", ns); - } - - // 5) OK... good to go for NS Deletion... - if (!rpdc.isEmpty()) { - for (PermDAO.Data perm : rpdc.value) { - deletePerm(trans, perm, true, true); - } - } - if (!rrdc.isEmpty()) { - for (RoleDAO.Data role : rrdc.value) { - deleteRole(trans, role, true, true); - } - } - - return q.nsDAO.delete(trans, nsd, false); - } - - public Result> getOwners(AuthzTrans trans, String ns, - boolean includeExpired) { - return getUsersByRole(trans, ns + Question.DOT_OWNER, includeExpired); - } - - private Result mayAddOwner(AuthzTrans trans, String ns, String id) { - Result rq = q.deriveNs(trans, ns); - if (rq.notOK()) { - return Result.err(rq); - } - - rq = q.mayUser(trans, trans.user(), rq.value, Access.write); - if (rq.notOK()) { - return Result.err(rq); - } - - Identity user; - Organization org = trans.org(); - try { - if ((user = org.getIdentity(trans, id)) == null) { - return Result.err(Status.ERR_Policy, - "%s reports that this is not a valid credential", - org.getName()); - } - String reason; - if ((reason=user.mayOwn())==null) { - return Result.ok(); - } else { - if (org.isTestEnv()) { - String reason2; - if((reason2 = org.validate(trans, Policy.AS_RESPONSIBLE, new CassExecutor(trans, this), id))==null) { - return Result.ok(); - } else { - trans.debug().log(reason2); - } - } - return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,user.fullName(),user.id(),ns, reason); - } - } catch (Exception e) { - return Result.err(e); - } - } - - private Result mayAddAdmin(AuthzTrans trans, String ns, String id) { - // Does NS Exist? - Result r = checkValidID(trans, new Date(), id); - if (r.notOK()) { - return r; - } - // Is id able to be an Admin - Result rq = q.deriveNs(trans, ns); - if (rq.notOK()) { - return Result.err(rq); - } - - rq = q.mayUser(trans, trans.user(), rq.value, Access.write); - if (rq.notOK()) { - Result> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner"); - if(!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) { - return Result.err(rq); - } - } - return r; - } - - private Result checkValidID(AuthzTrans trans, Date now, String user) { - Organization org = trans.org(); - if (org.supportsRealm(user)) { - try { - if (org.getIdentity(trans, user) == null) { - return Result.err(Status.ERR_Denied, - "%s reports that %s is a faulty ID", org.getName(), - user); - } - return Result.ok(); - } catch (Exception e) { - return Result.err(Result.ERR_Security, - "%s is not a valid %s Credential", user, org.getName()); - } - //TODO find out how to make sure good ALTERNATE OAUTH DOMAIN USER -// } else if(user.endsWith(ALTERNATE OAUTH DOMAIN)) { -// return Result.ok(); - } else { - Result> cdr = q.credDAO.readID(trans, user); - if (cdr.notOKorIsEmpty()) { - return Result.err(Status.ERR_Security, - "%s is not a valid AAF Credential", user); - } - - for (CredDAO.Data cd : cdr.value) { - if (cd.expires.after(now)) { - return Result.ok(); - } - } - } - return Result.err(Result.ERR_Security, "%s has expired", user); - } - - public Result delOwner(AuthzTrans trans, String ns, String id) { - Result rq = q.deriveNs(trans, ns); - if (rq.notOK()) { - return Result.err(rq); - } - - rq = q.mayUser(trans, trans.user(), rq.value, Access.write); - if (rq.notOK()) { - return Result.err(rq); - } - - return delUserRole(trans, id, ns,Question.OWNER); - } - - public Result> getAdmins(AuthzTrans trans, String ns, boolean includeExpired) { - return getUsersByRole(trans, ns + Question.DOT_ADMIN, includeExpired); - } - - public Result delAdmin(AuthzTrans trans, String ns, String id) { - Result rq = q.deriveNs(trans, ns); - if (rq.notOK()) { - return Result.err(rq); - } - - rq = q.mayUser(trans, trans.user(), rq.value, Access.write); - if (rq.notOK()) { - // Even though not a "writer", Owners still determine who gets to be an Admin - Result> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner"); - if(!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) { - return Result.err(rq); - } - } - - return delUserRole(trans, id, ns, Question.ADMIN); - } - - /** - * Helper function that moves permissions from a namespace being deleted to - * its parent namespace - * - * @param trans - * @param parent - * @param sb - * @param rpdc - * - list of permissions in namespace being deleted - */ - private void movePerms(AuthzTrans trans, NsDAO.Data parent, - StringBuilder sb, Result> rpdc) { - - Result rv; - Result pd; - - if (rpdc.isOKhasData()) { - for (PermDAO.Data pdd : rpdc.value) { - String delP2 = pdd.type; - if ("access".equals(delP2)) { - continue; - } - // Remove old Perm from Roles, save them off - List lrdd = new ArrayList<>(); - - for(String rl : pdd.roles(false)) { - Result rrdd = RoleDAO.Data.decode(trans,q,rl); - if(rrdd.isOKhasData()) { - RoleDAO.Data rdd = rrdd.value; - lrdd.add(rdd); - q.roleDAO.delPerm(trans, rdd, pdd); - } else{ - trans.error().log(rrdd.errorString()); - } - } - - // Save off Old keys - String delP1 = pdd.ns; - NsSplit nss = new NsSplit(parent, pdd.fullType()); - pdd.ns = nss.ns; - pdd.type = nss.name; - // Use direct Create/Delete, because switching namespaces - if ((pd = q.permDAO.create(trans, pdd)).isOK()) { - // Put Role back into Perm, with correct info - for(RoleDAO.Data rdd : lrdd) { - q.roleDAO.addPerm(trans, rdd, pdd); - } - - pdd.ns = delP1; - pdd.type = delP2; - if ((rv = q.permDAO.delete(trans, pdd, false)).notOK()) { - sb.append(rv.details); - sb.append('\n'); - // } else { - // Need to invalidate directly, because we're switching - // places in NS, not normal cache behavior - // q.permDAO.invalidate(trans,pdd); - } - } else { - sb.append(pd.details); - sb.append('\n'); - } - } - } - } - - /** - * Helper function that moves roles from a namespace being deleted to its - * parent namespace - * - * @param trans - * @param parent - * @param sb - * @param rrdc - * - list of roles in namespace being deleted - */ - private void moveRoles(AuthzTrans trans, NsDAO.Data parent, - StringBuilder sb, Result> rrdc) { - - Result rv; - Result rd; - - if (rrdc.isOKhasData()) { - for (RoleDAO.Data rdd : rrdc.value) { - String delP2 = rdd.name; - if ("admin".equals(delP2) || "owner".equals(delP2)) { - continue; - } - // Remove old Role from Perms, save them off - List lpdd = new ArrayList<>(); - for(String p : rdd.perms(false)) { - Result rpdd = PermDAO.Data.decode(trans,q,p); - if(rpdd.isOKhasData()) { - PermDAO.Data pdd = rpdd.value; - lpdd.add(pdd); - q.permDAO.delRole(trans, pdd, rdd); - } else{ - trans.error().log(rpdd.errorString()); - } - } - - // Save off Old keys - String delP1 = rdd.ns; - - NsSplit nss = new NsSplit(parent, rdd.fullName()); - rdd.ns = nss.ns; - rdd.name = nss.name; - // Use direct Create/Delete, because switching namespaces - if ((rd = q.roleDAO.create(trans, rdd)).isOK()) { - // Put Role back into Perm, with correct info - for(PermDAO.Data pdd : lpdd) { - q.permDAO.addRole(trans, pdd, rdd); - } - - rdd.ns = delP1; - rdd.name = delP2; - if ((rv = q.roleDAO.delete(trans, rdd, true)).notOK()) { - sb.append(rv.details); - sb.append('\n'); - // } else { - // Need to invalidate directly, because we're switching - // places in NS, not normal cache behavior - // q.roleDAO.invalidate(trans,rdd); - } - } else { - sb.append(rd.details); - sb.append('\n'); - } - } - } - } - - /** - * Create Permission (and any missing Permission between this and Parent) if - * we have permission - * - * Pass in the desired Management Permission for this Permission - * - * If Force is set, then Roles listed will be created, if allowed, - * pre-granted. - */ - public Result createPerm(AuthzTrans trans, PermDAO.Data perm, boolean fromApproval) { - String user = trans.user(); - // Next, see if User is allowed to Manage Parent Permission - - Result rnsd; - if (!fromApproval) { - rnsd = q.mayUser(trans, user, perm, Access.write); - if (rnsd.notOK()) { - return Result.err(rnsd); - } - } else { - rnsd = q.deriveNs(trans, perm.ns); - } - - // Does Child exist? - if (!trans.requested(REQD_TYPE.force)) { - if (q.permDAO.read(trans, perm).isOKhasData()) { - return Result.err(Status.ERR_ConflictAlreadyExists, - "Permission [%s.%s|%s|%s] already exists.", perm.ns, - perm.type, perm.instance, perm.action); - } - } - - // Attempt to add perms to roles, creating as possible - Set roles; - String pstring = perm.encode(); - - // For each Role - for (String role : roles = perm.roles(true)) { - Result rdd = RoleDAO.Data.decode(trans,q,role); - if(rdd.isOKhasData()) { - RoleDAO.Data rd = rdd.value; - if (!fromApproval) { - // May User write to the Role in question. - Result rns = q.mayUser(trans, user, rd, - Access.write); - if (rns.notOK()) { - // Remove the role from Add, because - roles.remove(role); // Don't allow adding - trans.warn() - .log("User [%s] does not have permission to relate Permissions to Role [%s]", - user, role); - } - } - - Result> rlrd; - if ((rlrd = q.roleDAO.read(trans, rd)).notOKorIsEmpty()) { - rd.perms(true).add(pstring); - if (q.roleDAO.create(trans, rd).notOK()) { - roles.remove(role); // Role doesn't exist, and can't be - // created - } - } else { - rd = rlrd.value.get(0); - if (!rd.perms.contains(pstring)) { - q.roleDAO.addPerm(trans, rd, perm); - } - } - } - } - - Result pdr = q.permDAO.create(trans, perm); - if (pdr.isOK()) { - return Result.ok(); - } else { - return Result.err(pdr); - } - } - - public Result deletePerm(final AuthzTrans trans, final PermDAO.Data perm, boolean force, boolean fromApproval) { - String user = trans.user(); - - // Next, see if User is allowed to Manage Permission - Result rnsd; - if (!fromApproval) { - rnsd = q.mayUser(trans, user, perm, Access.write); - if (rnsd.notOK()) { - return Result.err(rnsd); - } - } - // Does Perm exist? - Result> pdr = q.permDAO.read(trans, perm); - if (pdr.notOKorIsEmpty()) { - return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist.", - perm.ns,perm.type, perm.instance, perm.action); - } - // Get perm, but with rest of data. - PermDAO.Data fullperm = pdr.value.get(0); - - // Attached to any Roles? - if (fullperm.roles != null) { - if (force) { - for (String role : fullperm.roles) { - Result rv = null; - Result rrdd = RoleDAO.Data.decode(trans, q, role); - if(rrdd.isOKhasData()) { - trans.debug().log("Removing", role, "from", fullperm, "on Perm Delete"); - if ((rv = q.roleDAO.delPerm(trans, rrdd.value, fullperm)).notOK()) { - if (rv.notOK()) { - trans.error().log("Error removing Role during delFromPermRole: ", - trans.getUserPrincipal(), - rv.errorString()); - } - } - } else { - return Result.err(rrdd); - } - } - } else if (!fullperm.roles.isEmpty()) { - return Result - .err(Status.ERR_DependencyExists, - "Permission [%s.%s|%s|%s] cannot be deleted as it is attached to 1 or more roles.", - fullperm.ns, fullperm.type, fullperm.instance, fullperm.action); - } - } - - return q.permDAO.delete(trans, fullperm, false); - } - - public Result deleteRole(final AuthzTrans trans, final RoleDAO.Data role, boolean force, boolean fromApproval) { - String user = trans.user(); - - // Next, see if User is allowed to Manage Role - Result rnsd; - if (!fromApproval) { - rnsd = q.mayUser(trans, user, role, Access.write); - if (rnsd.notOK()) { - return Result.err(rnsd); - } - } - - // Are there any Users Attached to Role? - Result> urdr = q.userRoleDAO.readByRole(trans,role.fullName()); - if (force) { - if (urdr.isOKhasData()) { - for (UserRoleDAO.Data urd : urdr.value) { - q.userRoleDAO.delete(trans, urd, false); - } - } - } else if (urdr.isOKhasData()) { - return Result.err(Status.ERR_DependencyExists, - "Role [%s.%s] cannot be deleted as it is used by 1 or more Users.", - role.ns, role.name); - } - - // Does Role exist? - Result> rdr = q.roleDAO.read(trans, role); - if (rdr.notOKorIsEmpty()) { - return Result.err(Status.ERR_RoleNotFound, - "Role [%s.%s] does not exist", role.ns, role.name); - } - RoleDAO.Data fullrole = rdr.value.get(0); // full key search - - // Remove Self from Permissions... always, force or not. Force only applies to Dependencies (Users) - if (fullrole.perms != null) { - for (String perm : fullrole.perms(false)) { - Result rpd = PermDAO.Data.decode(trans,q,perm); - if (rpd.isOK()) { - trans.debug().log("Removing", perm, "from", fullrole,"on Role Delete"); - - Result r = q.permDAO.delRole(trans, rpd.value, fullrole); - if (r.notOK()) { - trans.error().log("ERR_FDR1 unable to remove",fullrole,"from",perm,':',r.status,'-',r.details); - } - } else { - trans.error().log("ERR_FDR2 Could not remove",perm,"from",fullrole); - } - } - } - return q.roleDAO.delete(trans, fullrole, false); - } - - /** - * Only owner of Permission may add to Role - * - * If force set, however, Role will be created before Grant, if User is - * allowed to create. - * - * @param trans - * @param role - * @param pd - * @return - */ - public Result addPermToRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) { - String user = trans.user(); - - if (!fromApproval) { - Result rRoleCo = q.deriveFirstNsForType(trans, role.ns, NsType.COMPANY); - if(rRoleCo.notOK()) { - return Result.err(rRoleCo); - } - Result rPermCo = q.deriveFirstNsForType(trans, pd.ns, NsType.COMPANY); - if(rPermCo.notOK()) { - return Result.err(rPermCo); - } - - // Not from same company - if(!rRoleCo.value.name.equals(rPermCo.value.name)) { - Result r; - // Only grant if User ALSO has Write ability in Other Company - if((r = q.mayUser(trans, user, role, Access.write)).notOK()) { - return Result.err(r); - } - } - - - // Must be Perm Admin, or Granted Special Permission - Result ucp = q.mayUser(trans, user, pd, Access.write); - if (ucp.notOK()) { - // Don't allow CLI potential Grantees to change their own AAF - // Perms, - if ((ROOT_NS.equals(pd.ns) && Question.NS.equals(pd.type)) - || !q.isGranted(trans, trans.user(),ROOT_NS,Question.PERM, rPermCo.value.name, "grant")) { - // Not otherwise granted - // TODO Needed? - return Result.err(ucp); - } - // Final Check... Don't allow Grantees to add to Roles they are - // part of - Result> rlurd = q.userRoleDAO - .readByUser(trans, trans.user()); - if (rlurd.isOK()) { - for (UserRoleDAO.Data ur : rlurd.value) { - if (role.ns.equals(ur.ns) && role.name.equals(ur.rname)) { - return Result.err(ucp); - } - } - } - } - } - - Result> rlpd = q.permDAO.read(trans, pd); - if (rlpd.notOKorIsEmpty()) { - return Result.err(Status.ERR_PermissionNotFound, - "Permission must exist to add to Role"); - } - - Result> rlrd = q.roleDAO.read(trans, role); // Already - // Checked - // for - // can - // change - // Role - Result rv; - - if (rlrd.notOKorIsEmpty()) { - if (trans.requested(REQD_TYPE.force)) { - Result ucr = q.mayUser(trans, user, role, - Access.write); - if (ucr.notOK()) { - return Result - .err(Status.ERR_Denied, - "Role [%s.%s] does not exist. User [%s] cannot create.", - role.ns, role.name, user); - } - - role.perms(true).add(pd.encode()); - Result rdd = q.roleDAO.create(trans, role); - if (rdd.isOK()) { - rv = Result.ok(); - } else { - rv = Result.err(rdd); - } - } else { - return Result.err(Status.ERR_RoleNotFound, - "Role [%s.%s] does not exist.", role.ns, role.name); - } - } else { - role = rlrd.value.get(0); - if (role.perms(false).contains(pd.encode())) { - return Result.err(Status.ERR_ConflictAlreadyExists, - "Permission [%s.%s] is already a member of role [%s,%s]", - pd.ns, pd.type, role.ns, role.name); - } - role.perms(true).add(pd.encode()); // this is added for Caching - // access purposes... doesn't - // affect addPerm - rv = q.roleDAO.addPerm(trans, role, pd); - } - if (rv.status == Status.OK) { - return q.permDAO.addRole(trans, pd, role); - // exploring how to add information message to successful http - // request - } - return rv; - } - - /** - * Either Owner of Role or Permission may delete from Role - * - * @param trans - * @param role - * @param pd - * @return - */ - public Result delPermFromRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) { - String user = trans.user(); - if (!fromApproval) { - Result ucr = q.mayUser(trans, user, role, Access.write); - Result ucp = q.mayUser(trans, user, pd, Access.write); - - // If Can't change either Role or Perm, then deny - if (ucr.notOK() && ucp.notOK()) { - return Result.err(Status.ERR_Denied, - "User [" + trans.user() - + "] does not have permission to delete [" - + pd.encode() + "] from Role [" - + role.fullName() + ']'); - } - } - - Result> rlr = q.roleDAO.read(trans, role); - if (rlr.notOKorIsEmpty()) { - // If Bad Data, clean out - Result> rlp = q.permDAO.read(trans, pd); - if (rlp.isOKhasData()) { - for (PermDAO.Data pv : rlp.value) { - q.permDAO.delRole(trans, pv, role); - } - } - return Result.err(rlr); - } - String perm1 = pd.encode(); - boolean notFound; - if (trans.requested(REQD_TYPE.force)) { - notFound = false; - } else { // only check if force not set. - notFound = true; - for (RoleDAO.Data r : rlr.value) { - if (r.perms != null) { - for (String perm : r.perms) { - if (perm1.equals(perm)) { - notFound = false; - break; - } - } - if(!notFound) { - break; - } - } - } - } - if (notFound) { // Need to check both, in case of corruption - return Result.err(Status.ERR_PermissionNotFound, - "Permission [%s.%s|%s|%s] not associated with any Role", - pd.ns,pd.type,pd.instance,pd.action); - } - - // Read Perm for full data - Result> rlp = q.permDAO.read(trans, pd); - Result rv = null; - if (rlp.isOKhasData()) { - for (PermDAO.Data pv : rlp.value) { - if ((rv = q.permDAO.delRole(trans, pv, role)).isOK()) { - if ((rv = q.roleDAO.delPerm(trans, role, pv)).notOK()) { - trans.error().log( - "Error removing Perm during delFromPermRole:", - trans.getUserPrincipal(), rv.errorString()); - } - } else { - trans.error().log( - "Error removing Role during delFromPermRole:", - trans.getUserPrincipal(), rv.errorString()); - } - } - } else { - rv = q.roleDAO.delPerm(trans, role, pd); - if (rv.notOK()) { - trans.error().log("Error removing Role during delFromPermRole", - rv.errorString()); - } - } - return rv == null ? Result.ok() : rv; - } - - public Result delPermFromRole(AuthzTrans trans, String role,PermDAO.Data pd) { - Result nss = q.deriveNsSplit(trans, role); - if (nss.notOK()) { - return Result.err(nss); - } - RoleDAO.Data rd = new RoleDAO.Data(); - rd.ns = nss.value.ns; - rd.name = nss.value.name; - return delPermFromRole(trans, rd, pd, false); - } - - /** - * Add a User to Role - * - * 1) Role must exist 2) User must be a known Credential (i.e. mechID ok if - * Credential) or known Organizational User - * - * @param trans - * @param org - * @param urData - * @return - * @throws DAOException - */ - public Result addUserRole(AuthzTrans trans,UserRoleDAO.Data urData) { - Result rv; - if(Question.ADMIN.equals(urData.rname)) { - rv = mayAddAdmin(trans, urData.ns, urData.user); - } else if(Question.OWNER.equals(urData.rname)) { - rv = mayAddOwner(trans, urData.ns, urData.user); - } else { - rv = checkValidID(trans, new Date(), urData.user); - } - if(rv.notOK()) { - return rv; - } - - // Check if record exists - if (q.userRoleDAO.read(trans, urData).isOKhasData()) { - return Result.err(Status.ERR_ConflictAlreadyExists, - "User Role exists"); - } - if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) { - return Result.err(Status.ERR_RoleNotFound, - "Role [%s.%s] does not exist", urData.ns, urData.rname); - } - - urData.expires = trans.org().expiration(null, Expiration.UserInRole, urData.user).getTime(); - - - Result udr = q.userRoleDAO.create(trans, urData); - switch (udr.status) { - case OK: - return Result.ok(); - default: - return Result.err(udr); - } - } - - public Result addUserRole(AuthzTrans trans, String user, String ns, String rname) { - try { - if(trans.org().getIdentity(trans, user)==null) { - return Result.err(Result.ERR_BadData,user+" is an Invalid Identity for " + trans.org().getName()); - } - } catch (OrganizationException e) { - return Result.err(e); - } - UserRoleDAO.Data urdd = new UserRoleDAO.Data(); - urdd.ns = ns; - urdd.role(ns, rname); - urdd.user = user; - return addUserRole(trans,urdd); - } - - /** - * Extend User Role. - * - * extend the Expiration data, according to Organization rules. - * - * @param trans - * @param org - * @param urData - * @return - */ - public Result extendUserRole(AuthzTrans trans, UserRoleDAO.Data urData, boolean checkForExist) { - // Check if record still exists - if (checkForExist && q.userRoleDAO.read(trans, urData).notOKorIsEmpty()) { - return Result.err(Status.ERR_UserRoleNotFound, - "User Role does not exist"); - } - - if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) { - return Result.err(Status.ERR_RoleNotFound, - "Role [%s.%s] does not exist", urData.ns,urData.rname); - } - // Special case for "Admin" roles. Issue brought forward with Prod - // problem 9/26 - Date now = new Date(); - GregorianCalendar gc = new GregorianCalendar(); - gc.setTime(now.after(urData.expires)?now:urData.expires); - urData.expires = trans.org().expiration(gc, Expiration.UserInRole).getTime(); // get - // Full - // time - // starting - // today - return q.userRoleDAO.update(trans, urData); - } - - // //////////////////////////////////////////////////// - // Special User Role Functions - // These exist, because User Roles have Expiration dates, which must be - // accounted for - // Also, as of July, 2015, Namespace Owners and Admins are now regular User - // Roles - // //////////////////////////////////////////////////// - public Result> getUsersByRole(AuthzTrans trans, String role, boolean includeExpired) { - Result> rurdd = q.userRoleDAO.readByRole(trans,role); - if (rurdd.notOK()) { - return Result.err(rurdd); - } - Date now = new Date(); - List list = rurdd.value; - List rv = new ArrayList<>(list.size()); // presize - for (UserRoleDAO.Data urdd : rurdd.value) { - if (includeExpired || urdd.expires.after(now)) { - rv.add(urdd.user); - } - } - return Result.ok(rv); - } - - public Result delUserRole(AuthzTrans trans, String user, String ns, String rname) { - UserRoleDAO.Data urdd = new UserRoleDAO.Data(); - urdd.user = user; - urdd.role(ns,rname); - Result> r = q.userRoleDAO.read(trans, urdd); - if (r.status == 404 || r.isEmpty()) { - return Result.err(Status.ERR_UserRoleNotFound, - "UserRole [%s] [%s.%s]", user, ns, rname); - } - if (r.notOK()) { - return Result.err(r); - } - - return q.userRoleDAO.delete(trans, urdd, false); - } - - public Result createFuture(AuthzTrans trans, FutureDAO.Data data, String id, String user, - NsDAO.Data nsd, FUTURE_OP op) { - StringBuilder sb = new StringBuilder(); - try { - Organization org = trans.org(); - // For Reapproval, only check Owners.. Do Supervisors, etc, separately - List approvers = op.equals(FUTURE_OP.A)?NO_ADDL_APPROVE:org.getApprovers(trans, user); - List owners = new ArrayList<>(); - if (nsd != null) { - Result> rrbr = q.userRoleDAO - .readByRole(trans, nsd.name + Question.DOT_OWNER); - if (rrbr.isOKhasData()) { - for(UserRoleDAO.Data urd : rrbr.value) { - Identity owner = org.getIdentity(trans, urd.user); - if(owner==null) { - return Result.err(Result.ERR_NotFound,urd.user + " is not a Valid Owner of " + nsd.name); - } else { - owners.add(owner); - } - } - } - } - - if(owners.isEmpty()) { - return Result.err(Result.ERR_NotFound,"No Owners found for " + nsd.name); - } - - // Create Future Object - - Result fr = q.futureDAO.create(trans, data, id); - if (fr.isOK()) { - sb.append("Created Future: "); - sb.append(data.id); - // User Future ID as ticket for Approvals - final UUID ticket = fr.value.id; - sb.append(", Approvals: "); - Boolean first[] = new Boolean[]{true}; - if(op!=FUTURE_OP.A) { - for (Identity u : approvers) { - Result r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,org.getApproverType()); - if(r.notOK()) { - return Result.err(r); - } - } - } - for (Identity u : owners) { - Result r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,"owner"); - if(r.notOK()) { - return Result.err(r); - } - } - } - } catch (Exception e) { - return Result.err(e); - } - - return Result.ok(sb.toString()); - } - - /* - * This interface is to allow performFutureOps with either Realtime Data, or Batched lookups (See Expiring) - */ - public interface Lookup { - T get(AuthzTrans trans, Object ... keys); - } - - public Lookup urDBLookup = new Lookup() { - @Override - public UserRoleDAO.Data get(AuthzTrans trans, Object ... keys) { - Result> r = q.userRoleDAO.read(trans, keys); - if(r.isOKhasData()) { - return r.value.get(0); - } else { - return null; - } - } - }; - - /** - * Note: if "allApprovals for Ticket is null, it will be looked up. - * if "fdd" is null, it will be looked up, but - * - * They can be passed for performance reasons. - * - * @param trans - * @param cd - * @param allApprovalsForTicket - * @return - */ - public Result performFutureOp(final AuthzTrans trans, FUTURE_OP fop, FutureDAO.Data curr, Lookup> la, Lookup lur) { - // Pre-Evaluate if ReApproval is already done. - UserRoleDAO.Data urdd = null; - if(fop.equals(FUTURE_OP.A) && curr.target.equals(FOP_USER_ROLE) && curr.construct!=null) { - try { - // Get Expected UserRole from Future - urdd = new UserRoleDAO.Data(); - urdd.reconstitute(curr.construct); - // Get Current UserRole from lookup - UserRoleDAO.Data lurdd = lur.get(trans, urdd.user,urdd.role); - if(lurdd==null) { - q.futureDAO.delete(trans, curr, false); - return OP_STATUS.RL; - } else { - if(curr.expires.compareTo(lurdd.expires)<0) { - q.futureDAO.delete(trans, curr, false); - return OP_STATUS.RL; - } - } - } catch (IOException e) { - return Result.err(Result.ERR_BadData,"Cannot reconstitute %1",curr.memo); - } - } - - boolean aDenial = false; - int cntSuper=0, appSuper=0,cntOwner=0, appOwner=0; - for(ApprovalDAO.Data add : la.get(trans)) { - switch(add.status) { - case "approved": - if("owner".equals(add.type)) { - ++cntOwner; - ++appOwner; - } else if("supervisor".equals(add.type)) { - ++cntSuper; - ++appSuper; - } - break; - case "pending": - if("owner".equals(add.type)) { - ++cntOwner; - } else if("supervisor".equals(add.type)) { - ++cntSuper; - } - break; - case "denied": - aDenial=true; - break; - } - } - - Result ros=null; - if(aDenial) { - // Note: Denial will be Audit-logged. -// for (ApprovalDAO.Data ad : allApprovalsForTicket.value) { -// q.approvalDAO.delete(trans, ad, false); -// } - ros = OP_STATUS.RD; - if(q.futureDAO.delete(trans, curr, false).notOK()) { - trans.info().printf("Future %s could not be deleted", curr.id.toString()); - } else { - if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) { - // A Denial means we must remove UserRole - if(fop.equals(FUTURE_OP.U) || fop.equals(FUTURE_OP.A)) { - UserRoleDAO.Data data = new UserRoleDAO.Data(); - try { - data.reconstitute(curr.construct); - } catch (IOException e) { - trans.error().log("Cannot reconstitue",curr.memo); - } - ros = set(OP_STATUS.RD,delUserRole(trans, data.user, data.ns, data.rname)); - } - } - } - } - - // Decision: If not Denied, and at least owner, if exists, and at least one Super, if exists - boolean goDecision = (cntOwner>0?appOwner>0:true) && (cntSuper>0?appSuper>0:true); - - if(goDecision) { - // should check if any other pendings before performing - // actions - try { - if (FOP_ROLE.equalsIgnoreCase(curr.target)) { - RoleDAO.Data data = new RoleDAO.Data(); - data.reconstitute(curr.construct); - switch(fop) { - case C: - ros = set(OP_STATUS.RE,q.roleDAO.dao().create(trans, data)); - break; - case D: - ros = set(OP_STATUS.RE,deleteRole(trans, data, true, true)); - break; - default: - } - } else if (FOP_PERM.equalsIgnoreCase(curr.target)) { - PermDAO.Data pdd = new PermDAO.Data(); - pdd.reconstitute(curr.construct); - Set roles; - Result rrdd; - switch(fop) { - case C: - ros = set(OP_STATUS.RE,createPerm(trans, pdd, true)); - break; - case D: - ros = set(OP_STATUS.RE,deletePerm(trans, pdd, true, true)); - break; - case G: - roles = pdd.roles(true); - for (String roleStr : roles) { - rrdd = RoleDAO.Data.decode(trans, q, roleStr); - if (rrdd.isOKhasData()) { - ros = set(OP_STATUS.RE,addPermToRole(trans, rrdd.value, pdd, true)); - } else { - trans.error().log(rrdd.errorString()); - } - } - break; - case UG: - roles = pdd.roles(true); - for (String roleStr : roles) { - rrdd = RoleDAO.Data.decode(trans, q, roleStr); - if (rrdd.isOKhasData()) { - ros = set(OP_STATUS.RE,delPermFromRole(trans, rrdd.value, pdd, true)); - } else { - trans.error().log(rrdd.errorString()); - } - } - break; - default: - } - } else if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) { - if(urdd==null) { - urdd = new UserRoleDAO.Data(); - urdd.reconstitute(curr.construct); - } - // if I am the last to approve, create user role - switch(fop) { - case C: - ros = set(OP_STATUS.RE,addUserRole(trans, urdd)); - break; - case U: - case A: - ros = set(OP_STATUS.RE,extendUserRole(trans,urdd,true)); - break; - default: - } - } else if (FOP_NS.equalsIgnoreCase(curr.target)) { - Namespace namespace = new Namespace(); - namespace.reconstitute(curr.construct); - switch(fop) { - case C: - ros = set(OP_STATUS.RE,createNS(trans, namespace, true)); - break; - default: - } - } else if (FOP_DELEGATE.equalsIgnoreCase(curr.target)) { - DelegateDAO.Data data = new DelegateDAO.Data(); - data.reconstitute(curr.construct); - switch(fop) { - case C: - ros = set(OP_STATUS.RE,q.delegateDAO.create(trans, data)); - break; - case U: - ros = set(OP_STATUS.RE,q.delegateDAO.update(trans, data)); - break; - default: - } - } else if (FOP_CRED.equalsIgnoreCase(curr.target)) { - CredDAO.Data data = new CredDAO.Data(); - data.reconstitute(curr.construct); - switch(fop) { - case C: - ros = set(OP_STATUS.RE,q.credDAO.dao().create(trans, data)); - break; - default: - } - } - } catch (Exception e) { - trans.error().log("Exception: ", e.getMessage(), - " \n occurred while performing", curr.memo, - " from Ticket ", curr.id.toString()); - } - q.futureDAO.delete(trans, curr, false); - } // end for goDecision - if(ros==null) { - //return Result.err(Status.ACC_Future, "Full Approvals not obtained: No action taken"); - ros = OP_STATUS.RP; - } - - return ros; - } - - // Convenience method for setting OPSTatus Results - private Result set(Result rs, Result orig) { - if(orig.isOK()) { - return rs; - } else { - return Result.err(orig); - } - } - - private Result addIdentity(AuthzTrans trans, StringBuilder sb, - Boolean[] first, String user, String memo, FUTURE_OP op, Identity u, UUID ticket, String type) throws OrganizationException { - ApprovalDAO.Data ad = new ApprovalDAO.Data(); - // Note ad.id is set by ApprovalDAO Create - ad.ticket = ticket; - ad.user = user; - ad.approver = u.fullID(); - ad.status = ApprovalDAO.PENDING; - ad.memo = memo; - ad.type = type; - ad.operation = op.name(); - // Note ad.updated is created in System - Result r = q.approvalDAO.create(trans,ad); - if(r.isOK()) { - if(first[0]) { - first[0] = false; - } else { - sb.append(", "); - } - sb.append(r.value.user); - sb.append(':'); - sb.append(r.value.ticket); - return r; - } else { - return Result.err(Status.ERR_ActionNotCompleted, - "Approval for %s, %s could not be created: %s", - ad.user, ad.approver, - r.details, sb.toString()); - } - } - - public Executor newExecutor(AuthzTrans trans) { - return new CassExecutor(trans, this); - } + private static final String CANNOT_BE_THE_OWNER_OF_A_NAMESPACE = "%s(%s) cannot be the owner of the namespace '%s'. Owners %s."; + + public enum FUTURE_OP { + C("Create"),U("Update"),D("Delete"),G("Grant"),UG("UnGrant"),A("Approval"); + + private String desc; + + private FUTURE_OP(String desc) { + this.desc = desc; + } + + public String desc() { + return desc; + } + + /** + * Same as valueOf(), but passes back null instead of throwing Exception + * @param value + * @return + */ + public static FUTURE_OP toFO(String value) { + if(value!=null) { + for(FUTURE_OP fo : values()) { + if(fo.name().equals(value)){ + return fo; + } + } + } + return null; + } + } + + public enum OP_STATUS { + E("Executed"),D("Denied"),P("Pending"),L("Lapsed"); + + private String desc; + public final static Result RE = Result.ok(OP_STATUS.E); + public final static Result RD = Result.ok(OP_STATUS.D); + public final static Result RP = Result.ok(OP_STATUS.P); + public final static Result RL = Result.ok(OP_STATUS.L); + + private OP_STATUS(String desc) { + this.desc = desc; + } + + public String desc() { + return desc; + } + + } + + public static final String FOP_CRED = "cred"; + public static final String FOP_DELEGATE = "delegate"; + public static final String FOP_NS = "ns"; + public static final String FOP_PERM = "perm"; + public static final String FOP_ROLE = "role"; + public static final String FOP_USER_ROLE = "user_role"; + private static final List NO_ADDL_APPROVE = new ArrayList<>(); + private static final String ROOT_NS = Define.ROOT_NS(); + // First Action should ALWAYS be "write", see "CreateRole" + public final Question q; + + public Function(AuthzTrans trans, Question question) { + q = question; + } + + private class ErrBuilder { + private StringBuilder sb; + private List ao; + + public void log(Result result) { + if (result.notOK()) { + if (sb == null) { + sb = new StringBuilder(); + ao = new ArrayList<>(); + } + sb.append(result.details); + sb.append('\n'); + for (String s : result.variables) { + ao.add(s); + } + } + } + + public String[] vars() { + String[] rv = new String[ao.size()]; + ao.toArray(rv); + return rv; + } + + public boolean hasErr() { + return sb != null; + } + + @Override + public String toString() { + return sb == null ? "" : String.format(sb.toString(), ao); + } + } + + /** + * createNS + * + * Create Namespace + * + * @param trans + * @param org + * @param ns + * @param user + * @return + * @throws DAOException + * + * To create an NS, you need to: 1) validate permission to + * modify parent NS 2) Does NS exist already? 3) Create NS with + * a) "user" as owner. NOTE: Per 10-15 request for AAF 1.0 4) + * Loop through Roles with Parent NS, and map any that start + * with this NS into this one 5) Loop through Perms with Parent + * NS, and map any that start with this NS into this one + */ + public Result createNS(AuthzTrans trans, Namespace namespace, boolean fromApproval) { + Result rq; +// if (namespace.name.endsWith(Question.DOT_ADMIN) +// || namespace.name.endsWith(Question.DOT_OWNER)) { +// return Result.err(Status.ERR_BadData, +// "'admin' and 'owner' are reserved names in AAF"); +// } + + try { + for (String u : namespace.owner) { + Organization org = trans.org(); + Identity orgUser = org.getIdentity(trans, u); + String reason; + if (orgUser == null) { + return Result.err(Status.ERR_Policy,"%s is not a valid user at %s",u,org.getName()); + } else if((reason=orgUser.mayOwn())!=null) { + if (org.isTestEnv()) { + String reason2; + if((reason2=org.validate(trans, Policy.AS_RESPONSIBLE,new CassExecutor(trans, this), u))!=null) { // can masquerade as responsible + trans.debug().log(reason2); + return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name,reason); + } + // a null means ok + } else { + if(orgUser.isFound()) { + return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name, reason); + } else { + return Result.err(Status.ERR_Policy,u + " is an invalid Identity"); + } + } + } + } + } catch (Exception e) { + trans.error().log(e, + "Could not contact Organization for User Validation"); + } + + String user = trans.user(); + // 1) May Change Parent? + int idx = namespace.name.lastIndexOf('.'); + String parent; + if (idx < 0) { + if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "create")) { + return Result.err(Result.ERR_Security, + "%s may not create Root Namespaces", user); + } + parent = null; + fromApproval = true; + } else { + parent = namespace.name.substring(0, idx); // get Parent String + } + + Result rparent = q.deriveNs(trans, parent); + if (rparent.notOK()) { + return Result.err(rparent); + } + if (!fromApproval) { + rparent = q.mayUser(trans, user, rparent.value, Access.write); + if (rparent.notOK()) { + return Result.err(rparent); + } + } + parent = namespace.parent = rparent.value.name; // Correct Namespace from real data + + // 2) Does requested NS exist + if (q.nsDAO.read(trans, namespace.name).isOKhasData()) { + return Result.err(Status.ERR_ConflictAlreadyExists, + "Target Namespace already exists"); + } + + // Someone must be responsible. + if (namespace.owner == null || namespace.owner.isEmpty()) { + return Result + .err(Status.ERR_Policy, + "Namespaces must be assigned at least one responsible party"); + } + + // 3) Create NS + Date now = new Date(); + + Result r; + // 3a) Admin + + try { + // Originally, added the enterer as Admin, but that's not necessary, + // or helpful for Operations folks.. + // Admins can be empty, because they can be changed by lower level + // NSs + // if(ns.admin(false).isEmpty()) { + // ns.admin(true).add(user); + // } + if (namespace.admin != null) { + for (String u : namespace.admin) { + if ((r = checkValidID(trans, now, u)).notOK()) { + return r; + } + } + } + + // 3b) Responsible + Organization org = trans.org(); + for (String u : namespace.owner) { + Identity orgUser = org.getIdentity(trans, u); + if (orgUser == null) { + return Result + .err(Status.ERR_BadData, + "NS must be created with an %s approved Responsible Party", + org.getName()); + } + } + } catch (Exception e) { + return Result.err(Status.ERR_UserNotFound, e.getMessage()); + } + + // VALIDATIONS done... Add NS + if ((rq = q.nsDAO.create(trans, namespace.data())).notOK()) { + return Result.err(rq); + } + + // Since Namespace is now created, we need to grab all subsequent errors + ErrBuilder eb = new ErrBuilder(); + + // Add UserRole(s) + UserRoleDAO.Data urdd = new UserRoleDAO.Data(); + urdd.expires = trans.org().expiration(null, Expiration.UserInRole).getTime(); + urdd.role(namespace.name, Question.ADMIN); + for (String admin : namespace.admin) { + urdd.user = admin; + eb.log(q.userRoleDAO.create(trans, urdd)); + } + urdd.role(namespace.name,Question.OWNER); + for (String owner : namespace.owner) { + urdd.user = owner; + eb.log(q.userRoleDAO.create(trans, urdd)); + } + + addNSAdminRolesPerms(trans, eb, namespace.name); + + addNSOwnerRolesPerms(trans, eb, namespace.name); + + if (parent != null) { + // Build up with any errors + + String targetNs = rparent.value.name; // Get the Parent Namespace, + // not target + String targetName = namespace.name.substring(targetNs.length() + 1); // Remove the Parent Namespace from the + // Target + a dot, and you'll get the name + int targetNameDot = targetName.length() + 1; + + // 4) Change any roles with children matching this NS, and + Result> rrdc = q.roleDAO.readChildren(trans, targetNs, targetName); + if (rrdc.isOKhasData()) { + for (RoleDAO.Data rdd : rrdc.value) { + // Remove old Role from Perms, save them off + List lpdd = new ArrayList<>(); + for(String p : rdd.perms(false)) { + Result rpdd = PermDAO.Data.decode(trans,q,p); + if(rpdd.isOKhasData()) { + PermDAO.Data pdd = rpdd.value; + lpdd.add(pdd); + q.permDAO.delRole(trans, pdd, rdd); + } else{ + trans.error().log(rpdd.errorString()); + } + } + + // Save off Old keys + String delP1 = rdd.ns; + String delP2 = rdd.name; + + // Write in new key + rdd.ns = namespace.name; + rdd.name = (delP2.length() > targetNameDot) ? delP2 + .substring(targetNameDot) : ""; + + // Need to use non-cached, because switching namespaces, not + // "create" per se + if ((rq = q.roleDAO.create(trans, rdd)).isOK()) { + // Put Role back into Perm, with correct info + for(PermDAO.Data pdd : lpdd) { + q.permDAO.addRole(trans, pdd, rdd); + } + // Change data for User Roles + Result> rurd = q.userRoleDAO.readByRole(trans, rdd.fullName()); + if(rurd.isOKhasData()) { + for(UserRoleDAO.Data urd : rurd.value) { + urd.ns = rdd.ns; + urd.rname = rdd.name; + q.userRoleDAO.update(trans, urd); + } + } + // Now delete old one + rdd.ns = delP1; + rdd.name = delP2; + if ((rq = q.roleDAO.delete(trans, rdd, false)).notOK()) { + eb.log(rq); + } + } else { + eb.log(rq); + } + } + } + + // 4) Change any Permissions with children matching this NS, and + Result> rpdc = q.permDAO.readChildren(trans,targetNs, targetName); + if (rpdc.isOKhasData()) { + for (PermDAO.Data pdd : rpdc.value) { + // Remove old Perm from Roles, save them off + List lrdd = new ArrayList<>(); + + for(String rl : pdd.roles(false)) { + Result rrdd = RoleDAO.Data.decode(trans,q,rl); + if(rrdd.isOKhasData()) { + RoleDAO.Data rdd = rrdd.value; + lrdd.add(rdd); + q.roleDAO.delPerm(trans, rdd, pdd); + } else{ + trans.error().log(rrdd.errorString()); + } + } + + // Save off Old keys + String delP1 = pdd.ns; + String delP2 = pdd.type; + pdd.ns = namespace.name; + pdd.type = (delP2.length() > targetNameDot) ? delP2 + .substring(targetNameDot) : ""; + if ((rq = q.permDAO.create(trans, pdd)).isOK()) { + // Put Role back into Perm, with correct info + for(RoleDAO.Data rdd : lrdd) { + q.roleDAO.addPerm(trans, rdd, pdd); + } + + pdd.ns = delP1; + pdd.type = delP2; + if ((rq = q.permDAO.delete(trans, pdd, false)).notOK()) { + eb.log(rq); + // } else { + // Need to invalidate directly, because we're + // switching places in NS, not normal cache behavior + // q.permDAO.invalidate(trans,pdd); + } + } else { + eb.log(rq); + } + } + } + if (eb.hasErr()) { + return Result.err(Status.ERR_ActionNotCompleted,eb.sb.toString(), eb.vars()); + } + } + return Result.ok(); + } + + private void addNSAdminRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) { + // Admin Role/Perm + RoleDAO.Data rd = new RoleDAO.Data(); + rd.ns = ns; + rd.name = "admin"; + rd.description = "AAF Namespace Administrators"; + + PermDAO.Data pd = new PermDAO.Data(); + pd.ns = ns; + pd.type = "access"; + pd.instance = Question.ASTERIX; + pd.action = Question.ASTERIX; + pd.description = "AAF Namespace Write Access"; + + rd.perms = new HashSet<>(); + rd.perms.add(pd.encode()); + eb.log(q.roleDAO.create(trans, rd)); + + pd.roles = new HashSet<>(); + pd.roles.add(rd.encode()); + eb.log(q.permDAO.create(trans, pd)); + } + + private void addNSOwnerRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) { + RoleDAO.Data rd = new RoleDAO.Data(); + rd.ns = ns; + rd.name = "owner"; + rd.description = "AAF Namespace Owners"; + + PermDAO.Data pd = new PermDAO.Data(); + pd.ns = ns; + pd.type = "access"; + pd.instance = Question.ASTERIX; + pd.action = Question.READ; + pd.description = "AAF Namespace Read Access"; + + rd.perms = new HashSet<>(); + rd.perms.add(pd.encode()); + eb.log(q.roleDAO.create(trans, rd)); + + pd.roles = new HashSet<>(); + pd.roles.add(rd.encode()); + eb.log(q.permDAO.create(trans, pd)); + } + + /** + * deleteNS + * + * Delete Namespace + * + * @param trans + * @param org + * @param ns + * @param force + * @param user + * @return + * @throws DAOException + * + * + * To delete an NS, you need to: 1) validate permission to + * modify this NS 2) Find all Roles with this NS, and 2a) if + * Force, delete them, else modify to Parent NS 3) Find all + * Perms with this NS, and modify to Parent NS 3a) if Force, + * delete them, else modify to Parent NS 4) Find all IDs + * associated to this NS, and deny if exists. 5) Remove NS + */ + public Result deleteNS(AuthzTrans trans, String ns) { + boolean force = trans.requested(REQD_TYPE.force); + boolean move = trans.requested(REQD_TYPE.move); + // 1) Validate + Result> nsl; + if ((nsl = q.nsDAO.read(trans, ns)).notOKorIsEmpty()) { + return Result.err(Status.ERR_NsNotFound, "%s does not exist", ns); + } + NsDAO.Data nsd = nsl.value.get(0); + NsType nt; + if (move && !q.canMove(nt = NsType.fromType(nsd.type))) { + return Result.err(Status.ERR_Denied, "Namespace Force=move not permitted for Type %s",nt.name()); + } + + Result dnr = q.mayUser(trans, trans.user(), nsd, Access.write); + if (dnr.status != Status.OK) { + return Result.err(dnr); + } + + // 2) Find Parent + String user = trans.user(); + int idx = ns.lastIndexOf('.'); + NsDAO.Data parent; + if (idx < 0) { + if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "delete")) { + return Result.err(Result.ERR_Security, + "%s may not delete Root Namespaces", user); + } + parent = null; + } else { + Result rlparent = q.deriveNs(trans, ns.substring(0, idx)); + if (rlparent.notOKorIsEmpty()) { + return Result.err(rlparent); + } + parent = rlparent.value; + } + + // Build up with any errors + // If sb != null below is an indication of error + StringBuilder sb = null; + ErrBuilder er = new ErrBuilder(); + + // 2a) Deny if any IDs on Namespace + Result> creds = q.credDAO.readNS(trans, ns); + if (creds.isOKhasData()) { + if (force || move) { + for (CredDAO.Data cd : creds.value) { + er.log(q.credDAO.delete(trans, cd, false)); + // Since we're deleting all the creds, we should delete all + // the user Roles for that Cred + Result> rlurd = q.userRoleDAO + .readByUser(trans, cd.id); + if (rlurd.isOK()) { + for (UserRoleDAO.Data data : rlurd.value) { + q.userRoleDAO.delete(trans, data, false); + } + } + + } + } else { + // first possible StringBuilder Create. + sb = new StringBuilder(); + sb.append('['); + sb.append(ns); + sb.append("] contains users"); + } + } + + // 2b) Find (or delete if forced flag is set) dependencies + // First, find if NS Perms are the only ones + Result> rpdc = q.permDAO.readNS(trans, ns); + if (rpdc.isOKhasData()) { + // Since there are now NS perms, we have to count NON-NS perms. + // FYI, if we delete them now, and the NS is not deleted, it is in + // an inconsistent state. + boolean nonaccess = false; + for (PermDAO.Data pdd : rpdc.value) { + if (!"access".equals(pdd.type)) { + nonaccess = true; + break; + } + } + if (nonaccess && !force && !move) { + if (sb == null) { + sb = new StringBuilder(); + sb.append('['); + sb.append(ns); + sb.append("] contains "); + } else { + sb.append(", "); + } + sb.append("permissions"); + } + } + + Result> rrdc = q.roleDAO.readNS(trans, ns); + if (rrdc.isOKhasData()) { + // Since there are now NS roles, we have to count NON-NS roles. + // FYI, if we delete th)em now, and the NS is not deleted, it is in + // an inconsistent state. + int count = rrdc.value.size(); + for (RoleDAO.Data rdd : rrdc.value) { + if ("admin".equals(rdd.name) || "owner".equals(rdd.name)) { + --count; + } + } + if (count > 0 && !force && !move) { + if (sb == null) { + sb = new StringBuilder(); + sb.append('['); + sb.append(ns); + sb.append("] contains "); + } else { + sb.append(", "); + } + sb.append("roles"); + } + } + + // 2c) Deny if dependencies exist that would be moved to root level + // parent is root level parent here. Need to find closest parent ns that + // exists + if (sb != null) { + if (!force && !move) { + sb.append(".\n Delete dependencies and try again. Note: using \"force=true\" will delete all. \"force=move\" will delete Creds, but move Roles and Perms to parent."); + return Result.err(Status.ERR_DependencyExists, sb.toString()); + } + + if (move && (parent == null || parent.type == NsType.COMPANY.type)) { + return Result + .err(Status.ERR_DependencyExists, + "Cannot move users, roles or permissions to [%s].\nDelete dependencies and try again", + parent.name); + } + } else if (move && parent != null) { + sb = new StringBuilder(); + // 3) Change any roles with children matching this NS, and + moveRoles(trans, parent, sb, rrdc); + // 4) Change any Perms with children matching this NS, and + movePerms(trans, parent, sb, rpdc); + } + + if (sb != null && sb.length() > 0) { + return Result.err(Status.ERR_DependencyExists, sb.toString()); + } + + if (er.hasErr()) { + if (trans.debug().isLoggable()) { + trans.debug().log(er.toString()); + } + return Result.err(Status.ERR_DependencyExists, + "Namespace members cannot be deleted for %s", ns); + } + + // 5) OK... good to go for NS Deletion... + if (!rpdc.isEmpty()) { + for (PermDAO.Data perm : rpdc.value) { + deletePerm(trans, perm, true, true); + } + } + if (!rrdc.isEmpty()) { + for (RoleDAO.Data role : rrdc.value) { + deleteRole(trans, role, true, true); + } + } + + return q.nsDAO.delete(trans, nsd, false); + } + + public Result> getOwners(AuthzTrans trans, String ns, + boolean includeExpired) { + return getUsersByRole(trans, ns + Question.DOT_OWNER, includeExpired); + } + + private Result mayAddOwner(AuthzTrans trans, String ns, String id) { + Result rq = q.deriveNs(trans, ns); + if (rq.notOK()) { + return Result.err(rq); + } + + rq = q.mayUser(trans, trans.user(), rq.value, Access.write); + if (rq.notOK()) { + return Result.err(rq); + } + + Identity user; + Organization org = trans.org(); + try { + if ((user = org.getIdentity(trans, id)) == null) { + return Result.err(Status.ERR_Policy, + "%s reports that this is not a valid credential", + org.getName()); + } + String reason; + if ((reason=user.mayOwn())==null) { + return Result.ok(); + } else { + if (org.isTestEnv()) { + String reason2; + if((reason2 = org.validate(trans, Policy.AS_RESPONSIBLE, new CassExecutor(trans, this), id))==null) { + return Result.ok(); + } else { + trans.debug().log(reason2); + } + } + return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,user.fullName(),user.id(),ns, reason); + } + } catch (Exception e) { + return Result.err(e); + } + } + + private Result mayAddAdmin(AuthzTrans trans, String ns, String id) { + // Does NS Exist? + Result r = checkValidID(trans, new Date(), id); + if (r.notOK()) { + return r; + } + // Is id able to be an Admin + Result rq = q.deriveNs(trans, ns); + if (rq.notOK()) { + return Result.err(rq); + } + + rq = q.mayUser(trans, trans.user(), rq.value, Access.write); + if (rq.notOK()) { + Result> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner"); + if(!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) { + return Result.err(rq); + } + } + return r; + } + + private Result checkValidID(AuthzTrans trans, Date now, String user) { + Organization org = trans.org(); + if (org.supportsRealm(user)) { + try { + if (org.getIdentity(trans, user) == null) { + return Result.err(Status.ERR_Denied, + "%s reports that %s is a faulty ID", org.getName(), + user); + } + return Result.ok(); + } catch (Exception e) { + return Result.err(Result.ERR_Security, + "%s is not a valid %s Credential", user, org.getName()); + } + //TODO find out how to make sure good ALTERNATE OAUTH DOMAIN USER +// } else if(user.endsWith(ALTERNATE OAUTH DOMAIN)) { +// return Result.ok(); + } else { + Result> cdr = q.credDAO.readID(trans, user); + if (cdr.notOKorIsEmpty()) { + return Result.err(Status.ERR_Security, + "%s is not a valid AAF Credential", user); + } + + for (CredDAO.Data cd : cdr.value) { + if (cd.expires.after(now)) { + return Result.ok(); + } + } + } + return Result.err(Result.ERR_Security, "%s has expired", user); + } + + public Result delOwner(AuthzTrans trans, String ns, String id) { + Result rq = q.deriveNs(trans, ns); + if (rq.notOK()) { + return Result.err(rq); + } + + rq = q.mayUser(trans, trans.user(), rq.value, Access.write); + if (rq.notOK()) { + return Result.err(rq); + } + + return delUserRole(trans, id, ns,Question.OWNER); + } + + public Result> getAdmins(AuthzTrans trans, String ns, boolean includeExpired) { + return getUsersByRole(trans, ns + Question.DOT_ADMIN, includeExpired); + } + + public Result delAdmin(AuthzTrans trans, String ns, String id) { + Result rq = q.deriveNs(trans, ns); + if (rq.notOK()) { + return Result.err(rq); + } + + rq = q.mayUser(trans, trans.user(), rq.value, Access.write); + if (rq.notOK()) { + // Even though not a "writer", Owners still determine who gets to be an Admin + Result> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner"); + if(!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) { + return Result.err(rq); + } + } + + return delUserRole(trans, id, ns, Question.ADMIN); + } + + /** + * Helper function that moves permissions from a namespace being deleted to + * its parent namespace + * + * @param trans + * @param parent + * @param sb + * @param rpdc + * - list of permissions in namespace being deleted + */ + private void movePerms(AuthzTrans trans, NsDAO.Data parent, + StringBuilder sb, Result> rpdc) { + + Result rv; + Result pd; + + if (rpdc.isOKhasData()) { + for (PermDAO.Data pdd : rpdc.value) { + String delP2 = pdd.type; + if ("access".equals(delP2)) { + continue; + } + // Remove old Perm from Roles, save them off + List lrdd = new ArrayList<>(); + + for(String rl : pdd.roles(false)) { + Result rrdd = RoleDAO.Data.decode(trans,q,rl); + if(rrdd.isOKhasData()) { + RoleDAO.Data rdd = rrdd.value; + lrdd.add(rdd); + q.roleDAO.delPerm(trans, rdd, pdd); + } else{ + trans.error().log(rrdd.errorString()); + } + } + + // Save off Old keys + String delP1 = pdd.ns; + NsSplit nss = new NsSplit(parent, pdd.fullType()); + pdd.ns = nss.ns; + pdd.type = nss.name; + // Use direct Create/Delete, because switching namespaces + if ((pd = q.permDAO.create(trans, pdd)).isOK()) { + // Put Role back into Perm, with correct info + for(RoleDAO.Data rdd : lrdd) { + q.roleDAO.addPerm(trans, rdd, pdd); + } + + pdd.ns = delP1; + pdd.type = delP2; + if ((rv = q.permDAO.delete(trans, pdd, false)).notOK()) { + sb.append(rv.details); + sb.append('\n'); + // } else { + // Need to invalidate directly, because we're switching + // places in NS, not normal cache behavior + // q.permDAO.invalidate(trans,pdd); + } + } else { + sb.append(pd.details); + sb.append('\n'); + } + } + } + } + + /** + * Helper function that moves roles from a namespace being deleted to its + * parent namespace + * + * @param trans + * @param parent + * @param sb + * @param rrdc + * - list of roles in namespace being deleted + */ + private void moveRoles(AuthzTrans trans, NsDAO.Data parent, + StringBuilder sb, Result> rrdc) { + + Result rv; + Result rd; + + if (rrdc.isOKhasData()) { + for (RoleDAO.Data rdd : rrdc.value) { + String delP2 = rdd.name; + if ("admin".equals(delP2) || "owner".equals(delP2)) { + continue; + } + // Remove old Role from Perms, save them off + List lpdd = new ArrayList<>(); + for(String p : rdd.perms(false)) { + Result rpdd = PermDAO.Data.decode(trans,q,p); + if(rpdd.isOKhasData()) { + PermDAO.Data pdd = rpdd.value; + lpdd.add(pdd); + q.permDAO.delRole(trans, pdd, rdd); + } else{ + trans.error().log(rpdd.errorString()); + } + } + + // Save off Old keys + String delP1 = rdd.ns; + + NsSplit nss = new NsSplit(parent, rdd.fullName()); + rdd.ns = nss.ns; + rdd.name = nss.name; + // Use direct Create/Delete, because switching namespaces + if ((rd = q.roleDAO.create(trans, rdd)).isOK()) { + // Put Role back into Perm, with correct info + for(PermDAO.Data pdd : lpdd) { + q.permDAO.addRole(trans, pdd, rdd); + } + + rdd.ns = delP1; + rdd.name = delP2; + if ((rv = q.roleDAO.delete(trans, rdd, true)).notOK()) { + sb.append(rv.details); + sb.append('\n'); + // } else { + // Need to invalidate directly, because we're switching + // places in NS, not normal cache behavior + // q.roleDAO.invalidate(trans,rdd); + } + } else { + sb.append(rd.details); + sb.append('\n'); + } + } + } + } + + /** + * Create Permission (and any missing Permission between this and Parent) if + * we have permission + * + * Pass in the desired Management Permission for this Permission + * + * If Force is set, then Roles listed will be created, if allowed, + * pre-granted. + */ + public Result createPerm(AuthzTrans trans, PermDAO.Data perm, boolean fromApproval) { + String user = trans.user(); + // Next, see if User is allowed to Manage Parent Permission + + Result rnsd; + if (!fromApproval) { + rnsd = q.mayUser(trans, user, perm, Access.write); + if (rnsd.notOK()) { + return Result.err(rnsd); + } + } else { + rnsd = q.deriveNs(trans, perm.ns); + } + + // Does Child exist? + if (!trans.requested(REQD_TYPE.force)) { + if (q.permDAO.read(trans, perm).isOKhasData()) { + return Result.err(Status.ERR_ConflictAlreadyExists, + "Permission [%s.%s|%s|%s] already exists.", perm.ns, + perm.type, perm.instance, perm.action); + } + } + + // Attempt to add perms to roles, creating as possible + Set roles; + String pstring = perm.encode(); + + // For each Role + for (String role : roles = perm.roles(true)) { + Result rdd = RoleDAO.Data.decode(trans,q,role); + if(rdd.isOKhasData()) { + RoleDAO.Data rd = rdd.value; + if (!fromApproval) { + // May User write to the Role in question. + Result rns = q.mayUser(trans, user, rd, + Access.write); + if (rns.notOK()) { + // Remove the role from Add, because + roles.remove(role); // Don't allow adding + trans.warn() + .log("User [%s] does not have permission to relate Permissions to Role [%s]", + user, role); + } + } + + Result> rlrd; + if ((rlrd = q.roleDAO.read(trans, rd)).notOKorIsEmpty()) { + rd.perms(true).add(pstring); + if (q.roleDAO.create(trans, rd).notOK()) { + roles.remove(role); // Role doesn't exist, and can't be + // created + } + } else { + rd = rlrd.value.get(0); + if (!rd.perms.contains(pstring)) { + q.roleDAO.addPerm(trans, rd, perm); + } + } + } + } + + Result pdr = q.permDAO.create(trans, perm); + if (pdr.isOK()) { + return Result.ok(); + } else { + return Result.err(pdr); + } + } + + public Result deletePerm(final AuthzTrans trans, final PermDAO.Data perm, boolean force, boolean fromApproval) { + String user = trans.user(); + + // Next, see if User is allowed to Manage Permission + Result rnsd; + if (!fromApproval) { + rnsd = q.mayUser(trans, user, perm, Access.write); + if (rnsd.notOK()) { + return Result.err(rnsd); + } + } + // Does Perm exist? + Result> pdr = q.permDAO.read(trans, perm); + if (pdr.notOKorIsEmpty()) { + return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist.", + perm.ns,perm.type, perm.instance, perm.action); + } + // Get perm, but with rest of data. + PermDAO.Data fullperm = pdr.value.get(0); + + // Attached to any Roles? + if (fullperm.roles != null) { + if (force) { + for (String role : fullperm.roles) { + Result rv = null; + Result rrdd = RoleDAO.Data.decode(trans, q, role); + if(rrdd.isOKhasData()) { + trans.debug().log("Removing", role, "from", fullperm, "on Perm Delete"); + if ((rv = q.roleDAO.delPerm(trans, rrdd.value, fullperm)).notOK()) { + if (rv.notOK()) { + trans.error().log("Error removing Role during delFromPermRole: ", + trans.getUserPrincipal(), + rv.errorString()); + } + } + } else { + return Result.err(rrdd); + } + } + } else if (!fullperm.roles.isEmpty()) { + return Result + .err(Status.ERR_DependencyExists, + "Permission [%s.%s|%s|%s] cannot be deleted as it is attached to 1 or more roles.", + fullperm.ns, fullperm.type, fullperm.instance, fullperm.action); + } + } + + return q.permDAO.delete(trans, fullperm, false); + } + + public Result deleteRole(final AuthzTrans trans, final RoleDAO.Data role, boolean force, boolean fromApproval) { + String user = trans.user(); + + // Next, see if User is allowed to Manage Role + Result rnsd; + if (!fromApproval) { + rnsd = q.mayUser(trans, user, role, Access.write); + if (rnsd.notOK()) { + return Result.err(rnsd); + } + } + + // Are there any Users Attached to Role? + Result> urdr = q.userRoleDAO.readByRole(trans,role.fullName()); + if (force) { + if (urdr.isOKhasData()) { + for (UserRoleDAO.Data urd : urdr.value) { + q.userRoleDAO.delete(trans, urd, false); + } + } + } else if (urdr.isOKhasData()) { + return Result.err(Status.ERR_DependencyExists, + "Role [%s.%s] cannot be deleted as it is used by 1 or more Users.", + role.ns, role.name); + } + + // Does Role exist? + Result> rdr = q.roleDAO.read(trans, role); + if (rdr.notOKorIsEmpty()) { + return Result.err(Status.ERR_RoleNotFound, + "Role [%s.%s] does not exist", role.ns, role.name); + } + RoleDAO.Data fullrole = rdr.value.get(0); // full key search + + // Remove Self from Permissions... always, force or not. Force only applies to Dependencies (Users) + if (fullrole.perms != null) { + for (String perm : fullrole.perms(false)) { + Result rpd = PermDAO.Data.decode(trans,q,perm); + if (rpd.isOK()) { + trans.debug().log("Removing", perm, "from", fullrole,"on Role Delete"); + + Result r = q.permDAO.delRole(trans, rpd.value, fullrole); + if (r.notOK()) { + trans.error().log("ERR_FDR1 unable to remove",fullrole,"from",perm,':',r.status,'-',r.details); + } + } else { + trans.error().log("ERR_FDR2 Could not remove",perm,"from",fullrole); + } + } + } + return q.roleDAO.delete(trans, fullrole, false); + } + + /** + * Only owner of Permission may add to Role + * + * If force set, however, Role will be created before Grant, if User is + * allowed to create. + * + * @param trans + * @param role + * @param pd + * @return + */ + public Result addPermToRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) { + String user = trans.user(); + + if (!fromApproval) { + Result rRoleCo = q.deriveFirstNsForType(trans, role.ns, NsType.COMPANY); + if(rRoleCo.notOK()) { + return Result.err(rRoleCo); + } + Result rPermCo = q.deriveFirstNsForType(trans, pd.ns, NsType.COMPANY); + if(rPermCo.notOK()) { + return Result.err(rPermCo); + } + + // Not from same company + if(!rRoleCo.value.name.equals(rPermCo.value.name)) { + Result r; + // Only grant if User ALSO has Write ability in Other Company + if((r = q.mayUser(trans, user, role, Access.write)).notOK()) { + return Result.err(r); + } + } + + + // Must be Perm Admin, or Granted Special Permission + Result ucp = q.mayUser(trans, user, pd, Access.write); + if (ucp.notOK()) { + // Don't allow CLI potential Grantees to change their own AAF + // Perms, + if ((ROOT_NS.equals(pd.ns) && Question.NS.equals(pd.type)) + || !q.isGranted(trans, trans.user(),ROOT_NS,Question.PERM, rPermCo.value.name, "grant")) { + // Not otherwise granted + // TODO Needed? + return Result.err(ucp); + } + // Final Check... Don't allow Grantees to add to Roles they are + // part of + Result> rlurd = q.userRoleDAO + .readByUser(trans, trans.user()); + if (rlurd.isOK()) { + for (UserRoleDAO.Data ur : rlurd.value) { + if (role.ns.equals(ur.ns) && role.name.equals(ur.rname)) { + return Result.err(ucp); + } + } + } + } + } + + Result> rlpd = q.permDAO.read(trans, pd); + if (rlpd.notOKorIsEmpty()) { + return Result.err(Status.ERR_PermissionNotFound, + "Permission must exist to add to Role"); + } + + Result> rlrd = q.roleDAO.read(trans, role); // Already + // Checked + // for + // can + // change + // Role + Result rv; + + if (rlrd.notOKorIsEmpty()) { + if (trans.requested(REQD_TYPE.force)) { + Result ucr = q.mayUser(trans, user, role, + Access.write); + if (ucr.notOK()) { + return Result + .err(Status.ERR_Denied, + "Role [%s.%s] does not exist. User [%s] cannot create.", + role.ns, role.name, user); + } + + role.perms(true).add(pd.encode()); + Result rdd = q.roleDAO.create(trans, role); + if (rdd.isOK()) { + rv = Result.ok(); + } else { + rv = Result.err(rdd); + } + } else { + return Result.err(Status.ERR_RoleNotFound, + "Role [%s.%s] does not exist.", role.ns, role.name); + } + } else { + role = rlrd.value.get(0); + if (role.perms(false).contains(pd.encode())) { + return Result.err(Status.ERR_ConflictAlreadyExists, + "Permission [%s.%s] is already a member of role [%s,%s]", + pd.ns, pd.type, role.ns, role.name); + } + role.perms(true).add(pd.encode()); // this is added for Caching + // access purposes... doesn't + // affect addPerm + rv = q.roleDAO.addPerm(trans, role, pd); + } + if (rv.status == Status.OK) { + return q.permDAO.addRole(trans, pd, role); + // exploring how to add information message to successful http + // request + } + return rv; + } + + /** + * Either Owner of Role or Permission may delete from Role + * + * @param trans + * @param role + * @param pd + * @return + */ + public Result delPermFromRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) { + String user = trans.user(); + if (!fromApproval) { + Result ucr = q.mayUser(trans, user, role, Access.write); + Result ucp = q.mayUser(trans, user, pd, Access.write); + + // If Can't change either Role or Perm, then deny + if (ucr.notOK() && ucp.notOK()) { + return Result.err(Status.ERR_Denied, + "User [" + trans.user() + + "] does not have permission to delete [" + + pd.encode() + "] from Role [" + + role.fullName() + ']'); + } + } + + Result> rlr = q.roleDAO.read(trans, role); + if (rlr.notOKorIsEmpty()) { + // If Bad Data, clean out + Result> rlp = q.permDAO.read(trans, pd); + if (rlp.isOKhasData()) { + for (PermDAO.Data pv : rlp.value) { + q.permDAO.delRole(trans, pv, role); + } + } + return Result.err(rlr); + } + String perm1 = pd.encode(); + boolean notFound; + if (trans.requested(REQD_TYPE.force)) { + notFound = false; + } else { // only check if force not set. + notFound = true; + for (RoleDAO.Data r : rlr.value) { + if (r.perms != null) { + for (String perm : r.perms) { + if (perm1.equals(perm)) { + notFound = false; + break; + } + } + if(!notFound) { + break; + } + } + } + } + if (notFound) { // Need to check both, in case of corruption + return Result.err(Status.ERR_PermissionNotFound, + "Permission [%s.%s|%s|%s] not associated with any Role", + pd.ns,pd.type,pd.instance,pd.action); + } + + // Read Perm for full data + Result> rlp = q.permDAO.read(trans, pd); + Result rv = null; + if (rlp.isOKhasData()) { + for (PermDAO.Data pv : rlp.value) { + if ((rv = q.permDAO.delRole(trans, pv, role)).isOK()) { + if ((rv = q.roleDAO.delPerm(trans, role, pv)).notOK()) { + trans.error().log( + "Error removing Perm during delFromPermRole:", + trans.getUserPrincipal(), rv.errorString()); + } + } else { + trans.error().log( + "Error removing Role during delFromPermRole:", + trans.getUserPrincipal(), rv.errorString()); + } + } + } else { + rv = q.roleDAO.delPerm(trans, role, pd); + if (rv.notOK()) { + trans.error().log("Error removing Role during delFromPermRole", + rv.errorString()); + } + } + return rv == null ? Result.ok() : rv; + } + + public Result delPermFromRole(AuthzTrans trans, String role,PermDAO.Data pd) { + Result nss = q.deriveNsSplit(trans, role); + if (nss.notOK()) { + return Result.err(nss); + } + RoleDAO.Data rd = new RoleDAO.Data(); + rd.ns = nss.value.ns; + rd.name = nss.value.name; + return delPermFromRole(trans, rd, pd, false); + } + + /** + * Add a User to Role + * + * 1) Role must exist 2) User must be a known Credential (i.e. mechID ok if + * Credential) or known Organizational User + * + * @param trans + * @param org + * @param urData + * @return + * @throws DAOException + */ + public Result addUserRole(AuthzTrans trans,UserRoleDAO.Data urData) { + Result rv; + if(Question.ADMIN.equals(urData.rname)) { + rv = mayAddAdmin(trans, urData.ns, urData.user); + } else if(Question.OWNER.equals(urData.rname)) { + rv = mayAddOwner(trans, urData.ns, urData.user); + } else { + rv = checkValidID(trans, new Date(), urData.user); + } + if(rv.notOK()) { + return rv; + } + + // Check if record exists + if (q.userRoleDAO.read(trans, urData).isOKhasData()) { + return Result.err(Status.ERR_ConflictAlreadyExists, + "User Role exists"); + } + if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) { + return Result.err(Status.ERR_RoleNotFound, + "Role [%s.%s] does not exist", urData.ns, urData.rname); + } + + urData.expires = trans.org().expiration(null, Expiration.UserInRole, urData.user).getTime(); + + + Result udr = q.userRoleDAO.create(trans, urData); + switch (udr.status) { + case OK: + return Result.ok(); + default: + return Result.err(udr); + } + } + + public Result addUserRole(AuthzTrans trans, String user, String ns, String rname) { + try { + if(trans.org().getIdentity(trans, user)==null) { + return Result.err(Result.ERR_BadData,user+" is an Invalid Identity for " + trans.org().getName()); + } + } catch (OrganizationException e) { + return Result.err(e); + } + UserRoleDAO.Data urdd = new UserRoleDAO.Data(); + urdd.ns = ns; + urdd.role(ns, rname); + urdd.user = user; + return addUserRole(trans,urdd); + } + + /** + * Extend User Role. + * + * extend the Expiration data, according to Organization rules. + * + * @param trans + * @param org + * @param urData + * @return + */ + public Result extendUserRole(AuthzTrans trans, UserRoleDAO.Data urData, boolean checkForExist) { + // Check if record still exists + if (checkForExist && q.userRoleDAO.read(trans, urData).notOKorIsEmpty()) { + return Result.err(Status.ERR_UserRoleNotFound, + "User Role does not exist"); + } + + if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) { + return Result.err(Status.ERR_RoleNotFound, + "Role [%s.%s] does not exist", urData.ns,urData.rname); + } + // Special case for "Admin" roles. Issue brought forward with Prod + // problem 9/26 + Date now = new Date(); + GregorianCalendar gc = new GregorianCalendar(); + gc.setTime(now.after(urData.expires)?now:urData.expires); + urData.expires = trans.org().expiration(gc, Expiration.UserInRole).getTime(); // get + // Full + // time + // starting + // today + return q.userRoleDAO.update(trans, urData); + } + + // //////////////////////////////////////////////////// + // Special User Role Functions + // These exist, because User Roles have Expiration dates, which must be + // accounted for + // Also, as of July, 2015, Namespace Owners and Admins are now regular User + // Roles + // //////////////////////////////////////////////////// + public Result> getUsersByRole(AuthzTrans trans, String role, boolean includeExpired) { + Result> rurdd = q.userRoleDAO.readByRole(trans,role); + if (rurdd.notOK()) { + return Result.err(rurdd); + } + Date now = new Date(); + List list = rurdd.value; + List rv = new ArrayList<>(list.size()); // presize + for (UserRoleDAO.Data urdd : rurdd.value) { + if (includeExpired || urdd.expires.after(now)) { + rv.add(urdd.user); + } + } + return Result.ok(rv); + } + + public Result delUserRole(AuthzTrans trans, String user, String ns, String rname) { + UserRoleDAO.Data urdd = new UserRoleDAO.Data(); + urdd.user = user; + urdd.role(ns,rname); + Result> r = q.userRoleDAO.read(trans, urdd); + if (r.status == 404 || r.isEmpty()) { + return Result.err(Status.ERR_UserRoleNotFound, + "UserRole [%s] [%s.%s]", user, ns, rname); + } + if (r.notOK()) { + return Result.err(r); + } + + return q.userRoleDAO.delete(trans, urdd, false); + } + + public Result createFuture(AuthzTrans trans, FutureDAO.Data data, String id, String user, + NsDAO.Data nsd, FUTURE_OP op) { + StringBuilder sb = new StringBuilder(); + try { + Organization org = trans.org(); + // For Reapproval, only check Owners.. Do Supervisors, etc, separately + List approvers = op.equals(FUTURE_OP.A)?NO_ADDL_APPROVE:org.getApprovers(trans, user); + List owners = new ArrayList<>(); + if (nsd != null) { + Result> rrbr = q.userRoleDAO + .readByRole(trans, nsd.name + Question.DOT_OWNER); + if (rrbr.isOKhasData()) { + for(UserRoleDAO.Data urd : rrbr.value) { + Identity owner = org.getIdentity(trans, urd.user); + if(owner==null) { + return Result.err(Result.ERR_NotFound,urd.user + " is not a Valid Owner of " + nsd.name); + } else { + owners.add(owner); + } + } + } + } + + if(owners.isEmpty()) { + return Result.err(Result.ERR_NotFound,"No Owners found for " + nsd.name); + } + + // Create Future Object + + Result fr = q.futureDAO.create(trans, data, id); + if (fr.isOK()) { + sb.append("Created Future: "); + sb.append(data.id); + // User Future ID as ticket for Approvals + final UUID ticket = fr.value.id; + sb.append(", Approvals: "); + Boolean first[] = new Boolean[]{true}; + if(op!=FUTURE_OP.A) { + for (Identity u : approvers) { + Result r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,org.getApproverType()); + if(r.notOK()) { + return Result.err(r); + } + } + } + for (Identity u : owners) { + Result r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,"owner"); + if(r.notOK()) { + return Result.err(r); + } + } + } + } catch (Exception e) { + return Result.err(e); + } + + return Result.ok(sb.toString()); + } + + /* + * This interface is to allow performFutureOps with either Realtime Data, or Batched lookups (See Expiring) + */ + public interface Lookup { + T get(AuthzTrans trans, Object ... keys); + } + + public Lookup urDBLookup = new Lookup() { + @Override + public UserRoleDAO.Data get(AuthzTrans trans, Object ... keys) { + Result> r = q.userRoleDAO.read(trans, keys); + if(r.isOKhasData()) { + return r.value.get(0); + } else { + return null; + } + } + }; + + /** + * Note: if "allApprovals for Ticket is null, it will be looked up. + * if "fdd" is null, it will be looked up, but + * + * They can be passed for performance reasons. + * + * @param trans + * @param cd + * @param allApprovalsForTicket + * @return + */ + public Result performFutureOp(final AuthzTrans trans, FUTURE_OP fop, FutureDAO.Data curr, Lookup> la, Lookup lur) { + // Pre-Evaluate if ReApproval is already done. + UserRoleDAO.Data urdd = null; + if(fop.equals(FUTURE_OP.A) && curr.target.equals(FOP_USER_ROLE) && curr.construct!=null) { + try { + // Get Expected UserRole from Future + urdd = new UserRoleDAO.Data(); + urdd.reconstitute(curr.construct); + // Get Current UserRole from lookup + UserRoleDAO.Data lurdd = lur.get(trans, urdd.user,urdd.role); + if(lurdd==null) { + q.futureDAO.delete(trans, curr, false); + return OP_STATUS.RL; + } else { + if(curr.expires.compareTo(lurdd.expires)<0) { + q.futureDAO.delete(trans, curr, false); + return OP_STATUS.RL; + } + } + } catch (IOException e) { + return Result.err(Result.ERR_BadData,"Cannot reconstitute %1",curr.memo); + } + } + + boolean aDenial = false; + int cntSuper=0, appSuper=0,cntOwner=0, appOwner=0; + for(ApprovalDAO.Data add : la.get(trans)) { + switch(add.status) { + case "approved": + if("owner".equals(add.type)) { + ++cntOwner; + ++appOwner; + } else if("supervisor".equals(add.type)) { + ++cntSuper; + ++appSuper; + } + break; + case "pending": + if("owner".equals(add.type)) { + ++cntOwner; + } else if("supervisor".equals(add.type)) { + ++cntSuper; + } + break; + case "denied": + aDenial=true; + break; + } + } + + Result ros=null; + if(aDenial) { + // Note: Denial will be Audit-logged. +// for (ApprovalDAO.Data ad : allApprovalsForTicket.value) { +// q.approvalDAO.delete(trans, ad, false); +// } + ros = OP_STATUS.RD; + if(q.futureDAO.delete(trans, curr, false).notOK()) { + trans.info().printf("Future %s could not be deleted", curr.id.toString()); + } else { + if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) { + // A Denial means we must remove UserRole + if(fop.equals(FUTURE_OP.U) || fop.equals(FUTURE_OP.A)) { + UserRoleDAO.Data data = new UserRoleDAO.Data(); + try { + data.reconstitute(curr.construct); + } catch (IOException e) { + trans.error().log("Cannot reconstitue",curr.memo); + } + ros = set(OP_STATUS.RD,delUserRole(trans, data.user, data.ns, data.rname)); + } + } + } + } + + // Decision: If not Denied, and at least owner, if exists, and at least one Super, if exists + boolean goDecision = (cntOwner>0?appOwner>0:true) && (cntSuper>0?appSuper>0:true); + + if(goDecision) { + // should check if any other pendings before performing + // actions + try { + if (FOP_ROLE.equalsIgnoreCase(curr.target)) { + RoleDAO.Data data = new RoleDAO.Data(); + data.reconstitute(curr.construct); + switch(fop) { + case C: + ros = set(OP_STATUS.RE,q.roleDAO.dao().create(trans, data)); + break; + case D: + ros = set(OP_STATUS.RE,deleteRole(trans, data, true, true)); + break; + default: + } + } else if (FOP_PERM.equalsIgnoreCase(curr.target)) { + PermDAO.Data pdd = new PermDAO.Data(); + pdd.reconstitute(curr.construct); + Set roles; + Result rrdd; + switch(fop) { + case C: + ros = set(OP_STATUS.RE,createPerm(trans, pdd, true)); + break; + case D: + ros = set(OP_STATUS.RE,deletePerm(trans, pdd, true, true)); + break; + case G: + roles = pdd.roles(true); + for (String roleStr : roles) { + rrdd = RoleDAO.Data.decode(trans, q, roleStr); + if (rrdd.isOKhasData()) { + ros = set(OP_STATUS.RE,addPermToRole(trans, rrdd.value, pdd, true)); + } else { + trans.error().log(rrdd.errorString()); + } + } + break; + case UG: + roles = pdd.roles(true); + for (String roleStr : roles) { + rrdd = RoleDAO.Data.decode(trans, q, roleStr); + if (rrdd.isOKhasData()) { + ros = set(OP_STATUS.RE,delPermFromRole(trans, rrdd.value, pdd, true)); + } else { + trans.error().log(rrdd.errorString()); + } + } + break; + default: + } + } else if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) { + if(urdd==null) { + urdd = new UserRoleDAO.Data(); + urdd.reconstitute(curr.construct); + } + // if I am the last to approve, create user role + switch(fop) { + case C: + ros = set(OP_STATUS.RE,addUserRole(trans, urdd)); + break; + case U: + case A: + ros = set(OP_STATUS.RE,extendUserRole(trans,urdd,true)); + break; + default: + } + } else if (FOP_NS.equalsIgnoreCase(curr.target)) { + Namespace namespace = new Namespace(); + namespace.reconstitute(curr.construct); + switch(fop) { + case C: + ros = set(OP_STATUS.RE,createNS(trans, namespace, true)); + break; + default: + } + } else if (FOP_DELEGATE.equalsIgnoreCase(curr.target)) { + DelegateDAO.Data data = new DelegateDAO.Data(); + data.reconstitute(curr.construct); + switch(fop) { + case C: + ros = set(OP_STATUS.RE,q.delegateDAO.create(trans, data)); + break; + case U: + ros = set(OP_STATUS.RE,q.delegateDAO.update(trans, data)); + break; + default: + } + } else if (FOP_CRED.equalsIgnoreCase(curr.target)) { + CredDAO.Data data = new CredDAO.Data(); + data.reconstitute(curr.construct); + switch(fop) { + case C: + ros = set(OP_STATUS.RE,q.credDAO.dao().create(trans, data)); + break; + default: + } + } + } catch (Exception e) { + trans.error().log("Exception: ", e.getMessage(), + " \n occurred while performing", curr.memo, + " from Ticket ", curr.id.toString()); + } + q.futureDAO.delete(trans, curr, false); + } // end for goDecision + if(ros==null) { + //return Result.err(Status.ACC_Future, "Full Approvals not obtained: No action taken"); + ros = OP_STATUS.RP; + } + + return ros; + } + + // Convenience method for setting OPSTatus Results + private Result set(Result rs, Result orig) { + if(orig.isOK()) { + return rs; + } else { + return Result.err(orig); + } + } + + private Result addIdentity(AuthzTrans trans, StringBuilder sb, + Boolean[] first, String user, String memo, FUTURE_OP op, Identity u, UUID ticket, String type) throws OrganizationException { + ApprovalDAO.Data ad = new ApprovalDAO.Data(); + // Note ad.id is set by ApprovalDAO Create + ad.ticket = ticket; + ad.user = user; + ad.approver = u.fullID(); + ad.status = ApprovalDAO.PENDING; + ad.memo = memo; + ad.type = type; + ad.operation = op.name(); + // Note ad.updated is created in System + Result r = q.approvalDAO.create(trans,ad); + if(r.isOK()) { + if(first[0]) { + first[0] = false; + } else { + sb.append(", "); + } + sb.append(r.value.user); + sb.append(':'); + sb.append(r.value.ticket); + return r; + } else { + return Result.err(Status.ERR_ActionNotCompleted, + "Approval for %s, %s could not be created: %s", + ad.user, ad.approver, + r.details, sb.toString()); + } + } + + public Executor newExecutor(AuthzTrans trans) { + return new CassExecutor(trans, this); + } }