Approval Batch, prep better JUnit
[aaf/authz.git] / auth / auth-cass / src / main / java / org / onap / aaf / auth / dao / hl / Function.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.dao.hl;
23
24 import static org.onap.aaf.auth.layer.Result.OK;
25
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Date;
29 import java.util.GregorianCalendar;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Set;
33 import java.util.UUID;
34
35 import org.onap.aaf.auth.common.Define;
36 import org.onap.aaf.auth.dao.DAOException;
37 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
38 import org.onap.aaf.auth.dao.cass.CredDAO;
39 import org.onap.aaf.auth.dao.cass.DelegateDAO;
40 import org.onap.aaf.auth.dao.cass.FutureDAO;
41 import org.onap.aaf.auth.dao.cass.Namespace;
42 import org.onap.aaf.auth.dao.cass.NsDAO;
43 import org.onap.aaf.auth.dao.cass.NsDAO.Data;
44 import org.onap.aaf.auth.dao.cass.NsSplit;
45 import org.onap.aaf.auth.dao.cass.NsType;
46 import org.onap.aaf.auth.dao.cass.PermDAO;
47 import org.onap.aaf.auth.dao.cass.RoleDAO;
48 import org.onap.aaf.auth.dao.cass.Status;
49 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
50 import org.onap.aaf.auth.dao.hl.Question.Access;
51 import org.onap.aaf.auth.env.AuthzTrans;
52 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
53 import org.onap.aaf.auth.layer.Result;
54 import org.onap.aaf.auth.org.Executor;
55 import org.onap.aaf.auth.org.Organization;
56 import org.onap.aaf.auth.org.Organization.Expiration;
57 import org.onap.aaf.auth.org.Organization.Identity;
58 import org.onap.aaf.auth.org.Organization.Policy;
59 import org.onap.aaf.auth.org.OrganizationException;
60
61 public class Function {
62
63     private static final String CANNOT_BE_THE_OWNER_OF_A_NAMESPACE = "%s(%s) cannot be the owner of the namespace '%s'. Owners %s.";
64
65     public enum FUTURE_OP {
66         C("Create"),U("Update"),D("Delete"),G("Grant"),UG("UnGrant"),A("Approval");
67         
68         private String desc;
69     
70         private FUTURE_OP(String desc) {
71             this.desc = desc;
72         }
73         
74         public String desc() {
75             return desc;
76         }
77         
78         /**
79          *  Same as valueOf(), but passes back null instead of throwing Exception
80          * @param value
81          * @return
82          */
83         public static FUTURE_OP toFO(String value) {
84             if (value!=null) {
85                 for (FUTURE_OP fo : values()) {
86                     if (fo.name().equals(value)){
87                         return fo;
88                     }
89                 }
90             }
91             return null;
92         }
93     }
94
95     public enum OP_STATUS {
96         E("Executed"),D("Denied"),P("Pending"),L("Lapsed");
97         
98         private String desc;
99         public final static Result<OP_STATUS> RE = Result.ok(OP_STATUS.E);
100         public final static Result<OP_STATUS> RD = Result.ok(OP_STATUS.D);
101         public final static Result<OP_STATUS> RP = Result.ok(OP_STATUS.P);
102         public final static Result<OP_STATUS> RL = Result.ok(OP_STATUS.L);
103
104         private OP_STATUS(String desc) {
105             this.desc = desc;
106         }
107         
108         public String desc() {
109             return desc;
110         }
111         
112     }
113
114     public static final String FOP_CRED = "cred";
115     public static final String FOP_DELEGATE = "delegate";
116     public static final String FOP_NS = "ns";
117     public static final String FOP_PERM = "perm";
118     public static final String FOP_ROLE = "role";
119     public static final String FOP_USER_ROLE = "user_role";
120     private static final List<Identity> NO_ADDL_APPROVE = new ArrayList<>();
121     private static final String ROOT_NS = Define.ROOT_NS();
122     // First Action should ALWAYS be "write", see "CreateRole"
123     public final Question q;
124
125     public Function(AuthzTrans trans, Question question) {
126         q = question;
127     }
128
129     private class ErrBuilder {
130         private StringBuilder sb;
131         private List<String> ao;
132
133         public void log(Result<?> result) {
134             if (result.notOK()) {
135                 if (sb == null) {
136                     sb = new StringBuilder();
137                     ao = new ArrayList<>();
138                 }
139                 sb.append(String.format(result.details,result.variables));
140                 sb.append('\n');
141             }
142         }
143
144         public String[] vars() {
145             String[] rv = new String[ao.size()];
146             ao.toArray(rv);
147             return rv;
148         }
149
150         public boolean hasErr() {
151             return sb != null;
152         }
153
154         @Override
155         public String toString() {
156             return sb == null ? "" : String.format(sb.toString(), ao);
157         }
158     }
159
160     /**
161      * createNS
162      * 
163      * Create Namespace
164      * 
165      * @param trans
166      * @param org
167      * @param ns
168      * @param user
169      * @return
170      * @throws DAOException
171      * 
172      *             To create an NS, you need to: 1) validate permission to
173      *             modify parent NS 2) Does NS exist already? 3) Create NS with
174      *             a) "user" as owner. NOTE: Per 10-15 request for AAF 1.0 4)
175      *             Loop through Roles with Parent NS, and map any that start
176      *             with this NS into this one 5) Loop through Perms with Parent
177      *             NS, and map any that start with this NS into this one
178      */
179     public Result<Void> createNS(AuthzTrans trans, Namespace namespace, boolean fromApproval) {
180         Result<?> rq;
181 //        if (namespace.name.endsWith(Question.DOT_ADMIN)
182 //                || namespace.name.endsWith(Question.DOT_OWNER)) {
183 //            return Result.err(Status.ERR_BadData,
184 //                    "'admin' and 'owner' are reserved names in AAF");
185 //        }
186
187         try {
188             for (String u : namespace.owner) {
189                 Organization org = trans.org();
190                 Identity orgUser = org.getIdentity(trans, u);
191                 String reason;
192                 if (orgUser == null) {
193                     return Result.err(Status.ERR_Policy,"%s is not a valid user at %s",u,org.getName());    
194                 } else if ((reason=orgUser.mayOwn())!=null) {
195                     if (org.isTestEnv()) {
196                         String reason2;
197                         if ((reason2=org.validate(trans, Policy.AS_RESPONSIBLE,new CassExecutor(trans, this), u))!=null) { // can masquerade as responsible
198                             trans.debug().log(reason2);
199                             return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name,reason);
200                         }
201                         // a null means ok
202                     } else {
203                         if (orgUser.isFound()) {
204                             return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,orgUser.fullName(),orgUser.id(),namespace.name, reason);
205                         } else {
206                             return Result.err(Status.ERR_Policy,u + " is an invalid Identity");
207                         }
208                     }
209                 }
210             }
211         } catch (Exception e) {
212             trans.error().log(e,
213                     "Could not contact Organization for User Validation");
214         }
215
216         String user = trans.user();
217         // 1) May Change Parent?
218         int idx = namespace.name.lastIndexOf('.');
219         String parent;
220         if (idx < 0) {
221             if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "create")) {
222                 return Result.err(Result.ERR_Security,
223                         "%s may not create Root Namespaces", user);
224             }
225             parent = null;
226             fromApproval = true;
227         } else {
228             parent = namespace.name.substring(0, idx); // get Parent String
229         }
230
231         Result<NsDAO.Data> rparent = q.deriveNs(trans, parent);
232         if (rparent.notOK()) {
233             return Result.err(rparent);
234         }
235         if (!fromApproval) {
236             rparent = q.mayUser(trans, user, rparent.value, Access.write);
237             if (rparent.notOK()) {
238                 return Result.err(rparent);
239             }
240         }
241         parent = namespace.parent = rparent.value.name; // Correct Namespace from real data
242
243         // 2) Does requested NS exist
244         if (q.nsDAO.read(trans, namespace.name).isOKhasData()) {
245             return Result.err(Status.ERR_ConflictAlreadyExists,
246                     "Target Namespace already exists");
247         }
248
249         // Someone must be responsible.
250         if (namespace.owner == null || namespace.owner.isEmpty()) {
251             return Result
252                     .err(Status.ERR_Policy,
253                             "Namespaces must be assigned at least one responsible party");
254         }
255
256         // 3) Create NS
257         Date now = new Date();
258
259         Result<Void> r;
260         // 3a) Admin
261
262         try {
263             // Originally, added the enterer as Admin, but that's not necessary,
264             // or helpful for Operations folks..
265             // Admins can be empty, because they can be changed by lower level
266             // NSs
267             // if (ns.admin(false).isEmpty()) {
268             // ns.admin(true).add(user);
269             // }
270             if (namespace.admin != null) {
271                 for (String u : namespace.admin) {
272                     if ((r = checkValidID(trans, now, u)).notOK()) {
273                         return r;
274                     }
275                 }
276             }
277
278             // 3b) Responsible
279             Organization org = trans.org();
280             for (String u : namespace.owner) {
281                 Identity orgUser = org.getIdentity(trans, u);
282                 if (orgUser == null) {
283                     return Result
284                             .err(Status.ERR_BadData,
285                                     "NS must be created with an %s approved Responsible Party",
286                                     org.getName());
287                 }
288             }
289         } catch (Exception e) {
290             return Result.err(Status.ERR_UserNotFound, e.getMessage());
291         }
292
293         // VALIDATIONS done... Add NS
294         if ((rq = q.nsDAO.create(trans, namespace.data())).notOK()) {
295             return Result.err(rq);
296         }
297
298         // Since Namespace is now created, we need to grab all subsequent errors
299         ErrBuilder eb = new ErrBuilder();
300
301         // Add UserRole(s)
302         UserRoleDAO.Data urdd = new UserRoleDAO.Data();
303         urdd.expires = trans.org().expiration(null, Expiration.UserInRole).getTime();
304         urdd.role(namespace.name, Question.ADMIN);
305         for (String admin : namespace.admin) {
306             urdd.user = admin;
307             eb.log(q.userRoleDAO.create(trans, urdd));
308         }
309         urdd.role(namespace.name,Question.OWNER);
310         for (String owner : namespace.owner) {
311             urdd.user = owner;
312             eb.log(q.userRoleDAO.create(trans, urdd));
313         }
314
315         addNSAdminRolesPerms(trans, eb, namespace.name);
316
317         addNSOwnerRolesPerms(trans, eb, namespace.name);
318
319         if (parent != null) {
320             // Build up with any errors
321
322             String targetNs = rparent.value.name; // Get the Parent Namespace,
323                                                     // not target
324             String targetName = namespace.name.substring(targetNs.length() + 1); // Remove the Parent Namespace from the
325                                     // Target + a dot, and you'll get the name
326             int targetNameDot = targetName.length() + 1;
327
328             // 4) Change any roles with children matching this NS, and
329             Result<List<RoleDAO.Data>> rrdc = q.roleDAO.readChildren(trans,    targetNs, targetName);
330             if (rrdc.isOKhasData()) {
331                 for (RoleDAO.Data rdd : rrdc.value) {
332                     // Remove old Role from Perms, save them off
333                     List<PermDAO.Data> lpdd = new ArrayList<>();
334                     for (String p : rdd.perms(false)) {
335                         Result<PermDAO.Data> rpdd = PermDAO.Data.decode(trans,q,p);
336                         if (rpdd.isOKhasData()) {
337                             PermDAO.Data pdd = rpdd.value;
338                             lpdd.add(pdd);
339                             q.permDAO.delRole(trans, pdd, rdd);
340                         } else{
341                             trans.error().log(rpdd.errorString());
342                         }
343                     }
344                     
345                     // Save off Old keys
346                     String delP1 = rdd.ns;
347                     String delP2 = rdd.name;
348
349                     // Write in new key
350                     rdd.ns = namespace.name;
351                     rdd.name = (delP2.length() > targetNameDot) ? delP2
352                             .substring(targetNameDot) : "";
353                             
354                     // Need to use non-cached, because switching namespaces, not
355                     // "create" per se
356                     if ((rq = q.roleDAO.create(trans, rdd)).isOK()) {
357                         // Put Role back into Perm, with correct info
358                         for (PermDAO.Data pdd : lpdd) {
359                             q.permDAO.addRole(trans, pdd, rdd);
360                         }
361                         // Change data for User Roles 
362                         Result<List<UserRoleDAO.Data>> rurd = q.userRoleDAO.readByRole(trans, rdd.fullName());
363                         if (rurd.isOKhasData()) {
364                             for (UserRoleDAO.Data urd : rurd.value) {
365                                 urd.ns = rdd.ns;
366                                 urd.rname = rdd.name;
367                                 q.userRoleDAO.update(trans, urd);
368                             }
369                         }
370                         // Now delete old one
371                         rdd.ns = delP1;
372                         rdd.name = delP2;
373                         if ((rq = q.roleDAO.delete(trans, rdd, false)).notOK()) {
374                             eb.log(rq);
375                         }
376                     } else {
377                         eb.log(rq);
378                     }
379                 }
380             }
381
382             // 4) Change any Permissions with children matching this NS, and
383             Result<List<PermDAO.Data>> rpdc = q.permDAO.readChildren(trans,targetNs, targetName);
384             if (rpdc.isOKhasData()) {
385                 for (PermDAO.Data pdd : rpdc.value) {
386                     // Remove old Perm from Roles, save them off
387                     List<RoleDAO.Data> lrdd = new ArrayList<>();
388                     
389                     for (String rl : pdd.roles(false)) {
390                         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,q,rl);
391                         if (rrdd.isOKhasData()) {
392                             RoleDAO.Data rdd = rrdd.value;
393                             lrdd.add(rdd);
394                             q.roleDAO.delPerm(trans, rdd, pdd);
395                         } else{
396                             trans.error().log(rrdd.errorString());
397                         }
398                     }
399                     
400                     // Save off Old keys
401                     String delP1 = pdd.ns;
402                     String delP2 = pdd.type;
403                     pdd.ns = namespace.name;
404                     pdd.type = (delP2.length() > targetNameDot) ? delP2
405                             .substring(targetNameDot) : "";
406                     if ((rq = q.permDAO.create(trans, pdd)).isOK()) {
407                         // Put Role back into Perm, with correct info
408                         for (RoleDAO.Data rdd : lrdd) {
409                             q.roleDAO.addPerm(trans, rdd, pdd);
410                         }
411
412                         pdd.ns = delP1;
413                         pdd.type = delP2;
414                         if ((rq = q.permDAO.delete(trans, pdd, false)).notOK()) {
415                             eb.log(rq);
416                             // } else {
417                             // Need to invalidate directly, because we're
418                             // switching places in NS, not normal cache behavior
419                             // q.permDAO.invalidate(trans,pdd);
420                         }
421                     } else {
422                         eb.log(rq);
423                     }
424                 }
425             }
426             if (eb.hasErr()) {
427                 return Result.err(Status.ERR_ActionNotCompleted,eb.sb.toString(), eb.vars());
428             }
429         }
430         return Result.ok();
431     }
432
433     private void addNSAdminRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) {
434         // Admin Role/Perm
435         RoleDAO.Data rd = new RoleDAO.Data();
436         rd.ns = ns;
437         rd.name = "admin";
438         rd.description = "AAF Namespace Administrators";
439
440         PermDAO.Data pd = new PermDAO.Data();
441         pd.ns = ns;
442         pd.type = "access";
443         pd.instance = Question.ASTERIX;
444         pd.action = Question.ASTERIX;
445         pd.description = "AAF Namespace Write Access";
446
447         rd.perms = new HashSet<>();
448         rd.perms.add(pd.encode());
449         eb.log(q.roleDAO.create(trans, rd));
450
451         pd.roles = new HashSet<>();
452         pd.roles.add(rd.encode());
453         eb.log(q.permDAO.create(trans, pd));
454     }
455
456     private void addNSOwnerRolesPerms(AuthzTrans trans, ErrBuilder eb, String ns) {
457         RoleDAO.Data rd = new RoleDAO.Data();
458         rd.ns = ns;
459         rd.name = "owner";
460         rd.description = "AAF Namespace Owners";
461
462         PermDAO.Data pd = new PermDAO.Data();
463         pd.ns = ns;
464         pd.type = "access";
465         pd.instance = Question.ASTERIX;
466         pd.action = Question.READ;
467         pd.description = "AAF Namespace Read Access";
468
469         rd.perms = new HashSet<>();
470         rd.perms.add(pd.encode());
471         eb.log(q.roleDAO.create(trans, rd));
472
473         pd.roles = new HashSet<>();
474         pd.roles.add(rd.encode());
475         eb.log(q.permDAO.create(trans, pd));
476     }
477
478     /**
479      * deleteNS
480      * 
481      * Delete Namespace
482      * 
483      * @param trans
484      * @param org
485      * @param ns
486      * @param force
487      * @param user
488      * @return
489      * @throws DAOException
490      * 
491      * 
492      *             To delete an NS, you need to: 1) validate permission to
493      *             modify this NS 2) Find all Roles with this NS, and 2a) if
494      *             Force, delete them, else modify to Parent NS 3) Find all
495      *             Perms with this NS, and modify to Parent NS 3a) if Force,
496      *             delete them, else modify to Parent NS 4) Find all IDs
497      *             associated to this NS, and deny if exists. 5) Remove NS
498      */
499     public Result<Void> deleteNS(AuthzTrans trans, String ns) {
500         boolean force = trans.requested(REQD_TYPE.force);
501         boolean move = trans.requested(REQD_TYPE.move);
502         // 1) Validate
503         Result<List<NsDAO.Data>> nsl;
504         if ((nsl = q.nsDAO.read(trans, ns)).notOKorIsEmpty()) {
505             return Result.err(Status.ERR_NsNotFound, "%s does not exist", ns);
506         }
507         NsDAO.Data nsd = nsl.value.get(0);
508         NsType nt;
509         if (move && !q.canMove(nt = NsType.fromType(nsd.type))) {
510             return Result.err(Status.ERR_Denied, "Namespace Force=move not permitted for Type %s",nt.name());
511         }
512
513         Result<NsDAO.Data> dnr = q.mayUser(trans, trans.user(), nsd, Access.write);
514         if (dnr.status != Status.OK) {
515             return Result.err(dnr);
516         }
517
518         // 2) Find Parent
519         String user = trans.user();
520         int idx = ns.lastIndexOf('.');
521         NsDAO.Data parent;
522         if (idx < 0) {
523             if (!q.isGranted(trans, user, ROOT_NS,Question.NS, ".", "delete")) {
524                 return Result.err(Result.ERR_Security,
525                         "%s may not delete Root Namespaces", user);
526             }
527             parent = null;
528         } else {
529             Result<NsDAO.Data> rlparent = q.deriveNs(trans,    ns.substring(0, idx));
530             if (rlparent.notOKorIsEmpty()) {
531                 return Result.err(rlparent);
532             }
533             parent = rlparent.value;
534         }
535
536         // Build up with any errors
537         // If sb != null below is an indication of error
538         StringBuilder sb = null;
539         ErrBuilder er = new ErrBuilder();
540
541         // 2a) Deny if any IDs on Namespace
542         Result<List<CredDAO.Data>> creds = q.credDAO.readNS(trans, ns);
543         if (creds.isOKhasData()) {
544             if (force || move) {
545                 for (CredDAO.Data cd : creds.value) {
546                     er.log(q.credDAO.delete(trans, cd, false));
547                     // Since we're deleting all the creds, we should delete all
548                     // the user Roles for that Cred
549                     Result<List<UserRoleDAO.Data>> rlurd = q.userRoleDAO
550                             .readByUser(trans, cd.id);
551                     if (rlurd.isOK()) {
552                         for (UserRoleDAO.Data data : rlurd.value) {
553                             q.userRoleDAO.delete(trans, data, false);
554                         }
555                     }
556
557                 }
558             } else {
559                 // first possible StringBuilder Create.
560                 sb = new StringBuilder();
561                 sb.append('[');
562                 sb.append(ns);
563                 sb.append("] contains users");
564             }
565         }
566
567         // 2b) Find (or delete if forced flag is set) dependencies
568         // First, find if NS Perms are the only ones
569         Result<List<PermDAO.Data>> rpdc = q.permDAO.readNS(trans, ns);
570         if (rpdc.isOKhasData()) {
571             // Since there are now NS perms, we have to count NON-NS perms.
572             // FYI, if we delete them now, and the NS is not deleted, it is in
573             // an inconsistent state.
574             boolean nonaccess = false;
575             for (PermDAO.Data pdd : rpdc.value) {
576                 if (!"access".equals(pdd.type)) {
577                     nonaccess = true;
578                     break;
579                 }
580             }
581             if (nonaccess && !force && !move) {
582                 if (sb == null) {
583                     sb = new StringBuilder();
584                     sb.append('[');
585                     sb.append(ns);
586                     sb.append("] contains ");
587                 } else {
588                     sb.append(", ");
589                 }
590                 sb.append("permissions");
591             }
592         }
593
594         Result<List<RoleDAO.Data>> rrdc = q.roleDAO.readNS(trans, ns);
595         if (rrdc.isOKhasData()) {
596             // Since there are now NS roles, we have to count NON-NS roles.
597             // FYI, if we delete th)em now, and the NS is not deleted, it is in
598             // an inconsistent state.
599             int count = rrdc.value.size();
600             for (RoleDAO.Data rdd : rrdc.value) {
601                 if ("admin".equals(rdd.name) || "owner".equals(rdd.name)) {
602                     --count;
603                 }
604             }
605             if (count > 0 && !force && !move) {
606                 if (sb == null) {
607                     sb = new StringBuilder();
608                     sb.append('[');
609                     sb.append(ns);
610                     sb.append("] contains ");
611                 } else {
612                     sb.append(", ");
613                 }
614                 sb.append("roles");
615             }
616         }
617
618         // 2c) Deny if dependencies exist that would be moved to root level
619         // parent is root level parent here. Need to find closest parent ns that
620         // exists
621         if (sb != null) {
622             if (!force && !move) {
623                 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.");
624                 return Result.err(Status.ERR_DependencyExists, sb.toString());
625             }
626
627             if (move && (parent == null || parent.type == NsType.COMPANY.type)) {
628                 return Result
629                         .err(Status.ERR_DependencyExists,
630                                 "Cannot move users, roles or permissions to [%s].\nDelete dependencies and try again",
631                                 parent.name);
632             }
633         } else if (move && parent != null) {
634             sb = new StringBuilder();
635             // 3) Change any roles with children matching this NS, and
636             moveRoles(trans, parent, sb, rrdc);
637             // 4) Change any Perms with children matching this NS, and
638             movePerms(trans, parent, sb, rpdc);
639         }
640
641         if (sb != null && sb.length() > 0) {
642             return Result.err(Status.ERR_DependencyExists, sb.toString());
643         }
644
645         if (er.hasErr()) {
646             if (trans.debug().isLoggable()) {
647                 trans.debug().log(er.toString());
648             }
649             return Result.err(Status.ERR_DependencyExists,
650                     "Namespace members cannot be deleted for %s", ns);
651         }
652
653         // 5) OK... good to go for NS Deletion...
654         if (!rpdc.isEmpty()) {
655             for (PermDAO.Data perm : rpdc.value) {
656                 deletePerm(trans, perm, true, true);
657             }
658         }
659         if (!rrdc.isEmpty()) {
660             for (RoleDAO.Data role : rrdc.value) {
661                 deleteRole(trans, role, true, true);
662             }
663         }
664
665         return q.nsDAO.delete(trans, nsd, false);
666     }
667
668     public Result<List<String>> getOwners(AuthzTrans trans, String ns,
669             boolean includeExpired) {
670         return getUsersByRole(trans, ns + Question.DOT_OWNER, includeExpired);
671     }
672
673     private Result<Void> mayAddOwner(AuthzTrans trans, String ns, String id) {
674         Result<NsDAO.Data> rq = q.deriveNs(trans, ns);
675         if (rq.notOK()) {
676             return Result.err(rq);
677         }
678
679         rq = q.mayUser(trans, trans.user(), rq.value, Access.write);
680         if (rq.notOK()) {
681             return Result.err(rq);
682         }
683
684         Identity user;
685         Organization org = trans.org();
686         try {
687             if ((user = org.getIdentity(trans, id)) == null) {
688                 return Result.err(Status.ERR_Policy,
689                         "%s reports that this is not a valid credential",
690                         org.getName());
691             }
692             String reason;
693             if ((reason=user.mayOwn())==null) {
694                 return Result.ok();
695             } else {
696                 if (org.isTestEnv()) {
697                     String reason2;
698                     if ((reason2 = org.validate(trans, Policy.AS_RESPONSIBLE, new CassExecutor(trans, this), id))==null) {
699                         return Result.ok();
700                     } else {
701                         trans.debug().log(reason2);
702                     }
703                 }
704                 return Result.err(Status.ERR_Policy,CANNOT_BE_THE_OWNER_OF_A_NAMESPACE,user.fullName(),user.id(),ns, reason);
705             }
706         } catch (Exception e) {
707             return Result.err(e);
708         }
709     }
710
711     private Result<Void> mayAddAdmin(AuthzTrans trans, String ns,    String id) {
712         // Does NS Exist?
713         Result<Void> r = checkValidID(trans, new Date(), id);
714         if (r.notOK()) {
715             return r;
716         }
717         // Is id able to be an Admin
718         Result<NsDAO.Data> rq = q.deriveNs(trans, ns);
719         if (rq.notOK()) {
720             return Result.err(rq);
721         }
722     
723         rq = q.mayUser(trans, trans.user(), rq.value, Access.write);
724         if (rq.notOK()) {
725             Result<List<UserRoleDAO.Data>> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner");
726             if (!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) {
727                 return Result.err(rq);
728             }
729         }
730         return r;
731     }
732
733     private Result<Void> checkValidID(AuthzTrans trans, Date now, String user) {
734         Organization org = trans.org();
735         if (org.supportsRealm(user)) {
736             try {
737                 if (org.getIdentity(trans, user) == null) {
738                     return Result.err(Status.ERR_Denied,
739                             "%s reports that %s is a faulty ID", org.getName(),
740                             user);
741                 }
742                 return Result.ok();
743             } catch (Exception e) {
744                 return Result.err(Result.ERR_Security,
745                         "%s is not a valid %s Credential", user, org.getName());
746             }
747         //TODO find out how to make sure good ALTERNATE OAUTH DOMAIN USER
748 //        } else if (user.endsWith(ALTERNATE OAUTH DOMAIN)) {
749 //            return Result.ok();
750         } else {
751             Result<List<CredDAO.Data>> cdr = q.credDAO.readID(trans, user);
752             if (cdr.notOKorIsEmpty()) {
753                 return Result.err(Status.ERR_Security,
754                         "%s is not a valid AAF Credential", user);
755             }
756     
757             for (CredDAO.Data cd : cdr.value) {
758                 if (cd.expires.after(now)) {
759                     return Result.ok();
760                 }
761             }
762         }
763         return Result.err(Result.ERR_Security, "%s has expired", user);
764     }
765
766     public Result<Void> delOwner(AuthzTrans trans, String ns, String id) {
767         Result<NsDAO.Data> rq = q.deriveNs(trans, ns);
768         if (rq.notOK()) {
769             return Result.err(rq);
770         }
771
772         rq = q.mayUser(trans, trans.user(), rq.value, Access.write);
773         if (rq.notOK()) {
774             return Result.err(rq);
775         }
776
777         return delUserRole(trans, id, ns,Question.OWNER);
778     }
779
780     public Result<List<String>> getAdmins(AuthzTrans trans, String ns, boolean includeExpired) {
781         return getUsersByRole(trans, ns + Question.DOT_ADMIN, includeExpired);
782     }
783
784     public Result<Void> delAdmin(AuthzTrans trans, String ns, String id) {
785         Result<NsDAO.Data> rq = q.deriveNs(trans, ns);
786         if (rq.notOK()) {
787             return Result.err(rq);
788         }
789
790         rq = q.mayUser(trans, trans.user(), rq.value, Access.write);
791         if (rq.notOK()) { 
792             // Even though not a "writer", Owners still determine who gets to be an Admin
793             Result<List<UserRoleDAO.Data>> ruinr = q.userRoleDAO.readUserInRole(trans, trans.user(),ns+".owner");
794             if (!(ruinr.isOKhasData() && ruinr.value.get(0).expires.after(new Date()))) {
795                 return Result.err(rq);
796             }
797         }
798
799         return delUserRole(trans, id, ns, Question.ADMIN);
800     }
801
802     /**
803      * Helper function that moves permissions from a namespace being deleted to
804      * its parent namespace
805      * 
806      * @param trans
807      * @param parent
808      * @param sb
809      * @param rpdc
810      *            - list of permissions in namespace being deleted
811      */
812     private void movePerms(AuthzTrans trans, NsDAO.Data parent,
813             StringBuilder sb, Result<List<PermDAO.Data>> rpdc) {
814
815         Result<Void> rv;
816         Result<PermDAO.Data> pd;
817
818         if (rpdc.isOKhasData()) {
819             for (PermDAO.Data pdd : rpdc.value) {
820                 String delP2 = pdd.type;
821                 if ("access".equals(delP2)) {
822                     continue;
823                 }
824                 // Remove old Perm from Roles, save them off
825                 List<RoleDAO.Data> lrdd = new ArrayList<>();
826                 
827                 for (String rl : pdd.roles(false)) {
828                     Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,q,rl);
829                     if (rrdd.isOKhasData()) {
830                         RoleDAO.Data rdd = rrdd.value;
831                         lrdd.add(rdd);
832                         q.roleDAO.delPerm(trans, rdd, pdd);
833                     } else{
834                         trans.error().log(rrdd.errorString());
835                     }
836                 }
837                 
838                 // Save off Old keys
839                 String delP1 = pdd.ns;
840                 NsSplit nss = new NsSplit(parent, pdd.fullType());
841                 pdd.ns = nss.ns;
842                 pdd.type = nss.name;
843                 // Use direct Create/Delete, because switching namespaces
844                 if ((pd = q.permDAO.create(trans, pdd)).isOK()) {
845                     // Put Role back into Perm, with correct info
846                     for (RoleDAO.Data rdd : lrdd) {
847                         q.roleDAO.addPerm(trans, rdd, pdd);
848                     }
849
850                     pdd.ns = delP1;
851                     pdd.type = delP2;
852                     if ((rv = q.permDAO.delete(trans, pdd, false)).notOK()) {
853                         sb.append(rv.details);
854                         sb.append('\n');
855                         // } else {
856                         // Need to invalidate directly, because we're switching
857                         // places in NS, not normal cache behavior
858                         // q.permDAO.invalidate(trans,pdd);
859                     }
860                 } else {
861                     sb.append(pd.details);
862                     sb.append('\n');
863                 }
864             }
865         }
866     }
867
868     /**
869      * Helper function that moves roles from a namespace being deleted to its
870      * parent namespace
871      * 
872      * @param trans
873      * @param parent
874      * @param sb
875      * @param rrdc
876      *            - list of roles in namespace being deleted
877      */
878     private void moveRoles(AuthzTrans trans, NsDAO.Data parent,
879             StringBuilder sb, Result<List<RoleDAO.Data>> rrdc) {
880
881         Result<Void> rv;
882         Result<RoleDAO.Data> rd;
883
884         if (rrdc.isOKhasData()) {
885             for (RoleDAO.Data rdd : rrdc.value) {
886                 String delP2 = rdd.name;
887                 if ("admin".equals(delP2) || "owner".equals(delP2)) {
888                     continue;
889                 }
890                 // Remove old Role from Perms, save them off
891                 List<PermDAO.Data> lpdd = new ArrayList<>();
892                 for (String p : rdd.perms(false)) {
893                     Result<PermDAO.Data> rpdd = PermDAO.Data.decode(trans,q,p);
894                     if (rpdd.isOKhasData()) {
895                         PermDAO.Data pdd = rpdd.value;
896                         lpdd.add(pdd);
897                         q.permDAO.delRole(trans, pdd, rdd);
898                     } else{
899                         trans.error().log(rpdd.errorString());
900                     }
901                 }
902                 
903                 // Save off Old keys
904                 String delP1 = rdd.ns;
905
906                 NsSplit nss = new NsSplit(parent, rdd.fullName());
907                 rdd.ns = nss.ns;
908                 rdd.name = nss.name;
909                 // Use direct Create/Delete, because switching namespaces
910                 if ((rd = q.roleDAO.create(trans, rdd)).isOK()) {
911                     // Put Role back into Perm, with correct info
912                     for (PermDAO.Data pdd : lpdd) {
913                         q.permDAO.addRole(trans, pdd, rdd);
914                     }
915
916                     rdd.ns = delP1;
917                     rdd.name = delP2;
918                     if ((rv = q.roleDAO.delete(trans, rdd, true)).notOK()) {
919                         sb.append(rv.details);
920                         sb.append('\n');
921                         // } else {
922                         // Need to invalidate directly, because we're switching
923                         // places in NS, not normal cache behavior
924                         // q.roleDAO.invalidate(trans,rdd);
925                     }
926                 } else {
927                     sb.append(rd.details);
928                     sb.append('\n');
929                 }
930             }
931         }
932     }
933
934     /**
935      * Create Permission (and any missing Permission between this and Parent) if
936      * we have permission
937      * 
938      * Pass in the desired Management Permission for this Permission
939      * 
940      * If Force is set, then Roles listed will be created, if allowed,
941      * pre-granted.
942      */
943     public Result<Void> createPerm(AuthzTrans trans, PermDAO.Data perm, boolean fromApproval) {
944         String user = trans.user();
945         // Next, see if User is allowed to Manage Parent Permission
946
947         Result<NsDAO.Data> rnsd;
948         if (!fromApproval) {
949             rnsd = q.mayUser(trans, user, perm, Access.write);
950             if (rnsd.notOK()) {
951                 return Result.err(rnsd);
952             }
953         } else {
954             rnsd = q.deriveNs(trans, perm.ns);
955         }
956
957         // Does Child exist?
958         if (!trans.requested(REQD_TYPE.force)) {
959             if (q.permDAO.read(trans, perm).isOKhasData()) {
960                 return Result.err(Status.ERR_ConflictAlreadyExists,
961                         "Permission [%s.%s|%s|%s] already exists.", perm.ns,
962                         perm.type, perm.instance, perm.action);
963             }
964         }
965
966         // Attempt to add perms to roles, creating as possible
967         Set<String> roles;
968         String pstring = perm.encode();
969
970         // For each Role
971         for (String role : roles = perm.roles(true)) {
972             Result<RoleDAO.Data> rdd = RoleDAO.Data.decode(trans,q,role);
973             if (rdd.isOKhasData()) {
974                 RoleDAO.Data rd = rdd.value;
975                 if (!fromApproval) {
976                     // May User write to the Role in question.
977                     Result<NsDAO.Data> rns = q.mayUser(trans, user, rd,
978                             Access.write);
979                     if (rns.notOK()) {
980                         // Remove the role from Add, because
981                         roles.remove(role); // Don't allow adding
982                         trans.warn()
983                                 .log("User [%s] does not have permission to relate Permissions to Role [%s]",
984                                         user, role);
985                     }
986                 }
987
988                 Result<List<RoleDAO.Data>> rlrd;
989                 if ((rlrd = q.roleDAO.read(trans, rd)).notOKorIsEmpty()) {
990                     rd.perms(true).add(pstring);
991                     if (q.roleDAO.create(trans, rd).notOK()) {
992                         roles.remove(role); // Role doesn't exist, and can't be
993                                             // created
994                     }
995                 } else {
996                     rd = rlrd.value.get(0);
997                     if (!rd.perms.contains(pstring)) {
998                         q.roleDAO.addPerm(trans, rd, perm);
999                     }
1000                 }
1001             }
1002         }
1003
1004         Result<PermDAO.Data> pdr = q.permDAO.create(trans, perm);
1005         if (pdr.isOK()) {
1006             return Result.ok();
1007         } else { 
1008             return Result.err(pdr);
1009         }
1010     }
1011
1012     public Result<Void> deletePerm(final AuthzTrans trans, final PermDAO.Data perm, boolean force, boolean fromApproval) {
1013         String user = trans.user();
1014
1015         // Next, see if User is allowed to Manage Permission
1016         Result<NsDAO.Data> rnsd;
1017         if (!fromApproval) {
1018             rnsd = q.mayUser(trans, user, perm, Access.write);
1019             if (rnsd.notOK()) {
1020                 return Result.err(rnsd);
1021             }
1022         }
1023         // Does Perm exist?
1024         Result<List<PermDAO.Data>> pdr = q.permDAO.read(trans, perm);
1025         if (pdr.notOKorIsEmpty()) {
1026             return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist.",
1027                     perm.ns,perm.type, perm.instance, perm.action);
1028         }
1029         // Get perm, but with rest of data.
1030         PermDAO.Data fullperm = pdr.value.get(0);
1031
1032         // Attached to any Roles?
1033         if (fullperm.roles != null) {
1034             if (force) {
1035                 for (String role : fullperm.roles) {
1036                     Result<Void> rv = null;
1037                     Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, q, role);
1038                     if (rrdd.isOKhasData()) {
1039                         trans.debug().log("Removing", role, "from", fullperm, "on Perm Delete");
1040                         if ((rv = q.roleDAO.delPerm(trans, rrdd.value, fullperm)).notOK()) {
1041                             if (rv.notOK()) {
1042                                 trans.error().log("Error removing Role during delFromPermRole: ",
1043                                                 trans.getUserPrincipal(),
1044                                                 rv.errorString());
1045                             }
1046                         }
1047                     } else {
1048                         return Result.err(rrdd);
1049                     }
1050                 }
1051             } else if (!fullperm.roles.isEmpty()) {
1052                 return Result
1053                         .err(Status.ERR_DependencyExists,
1054                                 "Permission [%s.%s|%s|%s] cannot be deleted as it is attached to 1 or more roles.",
1055                                 fullperm.ns, fullperm.type, fullperm.instance, fullperm.action);
1056             }
1057         }
1058
1059         return q.permDAO.delete(trans, fullperm, false);
1060     }
1061
1062     public Result<Void> deleteRole(final AuthzTrans trans, final RoleDAO.Data role, boolean force, boolean fromApproval) {
1063         String user = trans.user();
1064
1065         // Next, see if User is allowed to Manage Role
1066         Result<NsDAO.Data> rnsd;
1067         if (!fromApproval) {
1068             rnsd = q.mayUser(trans, user, role, Access.write);
1069             if (rnsd.notOK()) {
1070                 return Result.err(rnsd);
1071             }
1072         }
1073
1074         // Are there any Users Attached to Role?
1075         Result<List<UserRoleDAO.Data>> urdr = q.userRoleDAO.readByRole(trans,role.fullName());
1076         if (force) {
1077             if (urdr.isOKhasData()) {
1078                 for (UserRoleDAO.Data urd : urdr.value) {
1079                     q.userRoleDAO.delete(trans, urd, false);
1080                 }
1081             }
1082         } else if (urdr.isOKhasData()) {
1083             return Result.err(Status.ERR_DependencyExists,
1084                             "Role [%s.%s] cannot be deleted as it is used by 1 or more Users.",
1085                             role.ns, role.name);
1086         }
1087
1088         // Does Role exist?
1089         Result<List<RoleDAO.Data>> rdr = q.roleDAO.read(trans, role);
1090         if (rdr.notOKorIsEmpty()) {
1091             return Result.err(Status.ERR_RoleNotFound,
1092                     "Role [%s.%s] does not exist", role.ns, role.name);
1093         }
1094         RoleDAO.Data fullrole = rdr.value.get(0); // full key search
1095
1096         // Remove Self from Permissions... always, force or not.  Force only applies to Dependencies (Users)
1097         if (fullrole.perms != null) {
1098             for (String perm : fullrole.perms(false)) {
1099                 Result<PermDAO.Data> rpd = PermDAO.Data.decode(trans,q,perm);
1100                 if (rpd.isOK()) {
1101                     trans.debug().log("Removing", perm, "from", fullrole,"on Role Delete");
1102
1103                     Result<?> r = q.permDAO.delRole(trans, rpd.value, fullrole);
1104                     if (r.notOK()) {
1105                         trans.error().log("ERR_FDR1 unable to remove",fullrole,"from",perm,':',r.status,'-',r.details);
1106                     }
1107                 } else {
1108                     trans.error().log("ERR_FDR2 Could not remove",perm,"from",fullrole);
1109                 }
1110             }
1111         }
1112         return q.roleDAO.delete(trans, fullrole, false);
1113     }
1114
1115     /**
1116      * Only owner of Permission may add to Role
1117      * 
1118      * If force set, however, Role will be created before Grant, if User is
1119      * allowed to create.
1120      * 
1121      * @param trans
1122      * @param role
1123      * @param pd
1124      * @return
1125      */
1126     public Result<Void> addPermToRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) {
1127         String user = trans.user();
1128         
1129         if (!fromApproval) {
1130             Result<NsDAO.Data> rRoleCo = q.deriveFirstNsForType(trans, role.ns, NsType.COMPANY);
1131             if (rRoleCo.notOK()) {
1132                 return Result.err(rRoleCo);
1133             }
1134             Result<NsDAO.Data> rPermCo = q.deriveFirstNsForType(trans, pd.ns, NsType.COMPANY);
1135             if (rPermCo.notOK()) {
1136                 return Result.err(rPermCo);
1137             }
1138
1139             // Not from same company
1140             if (!rRoleCo.value.name.equals(rPermCo.value.name)) {
1141                 Result<Data> r;
1142                 // Only grant if User ALSO has Write ability in Other Company
1143                 if ((r = q.mayUser(trans, user, role, Access.write)).notOK()) {
1144                     return Result.err(r);
1145                 }
1146             }
1147             
1148
1149             // Must be Perm Admin, or Granted Special Permission
1150             Result<NsDAO.Data> ucp = q.mayUser(trans, user, pd, Access.write);
1151             if (ucp.notOK()) {
1152                 // Don't allow CLI potential Grantees to change their own AAF
1153                 // Perms,
1154                 if ((ROOT_NS.equals(pd.ns) && Question.NS.equals(pd.type)) 
1155                         || !q.isGranted(trans, trans.user(),ROOT_NS,Question.PERM, rPermCo.value.name, "grant")) {
1156                 // Not otherwise granted
1157                 // TODO Needed?
1158                     return Result.err(ucp);
1159                 }
1160                 // Final Check... Don't allow Grantees to add to Roles they are
1161                 // part of
1162                 Result<List<UserRoleDAO.Data>> rlurd = q.userRoleDAO
1163                         .readByUser(trans, trans.user());
1164                 if (rlurd.isOK()) {
1165                     for (UserRoleDAO.Data ur : rlurd.value) {
1166                         if (role.ns.equals(ur.ns) && role.name.equals(ur.rname)) {
1167                             return Result.err(ucp);
1168                         }
1169                     }
1170                 }
1171             }
1172         }
1173
1174         Result<List<PermDAO.Data>> rlpd = q.permDAO.read(trans, pd);
1175         if (rlpd.notOKorIsEmpty()) {
1176             return Result.err(Status.ERR_PermissionNotFound,
1177                     "Permission must exist to add to Role");
1178         }
1179
1180         Result<List<RoleDAO.Data>> rlrd = q.roleDAO.read(trans, role); // Already
1181                                                                         // Checked
1182                                                                         // for
1183                                                                         // can
1184                                                                         // change
1185                                                                         // Role
1186         Result<Void> rv;
1187
1188         if (rlrd.notOKorIsEmpty()) {
1189             if (trans.requested(REQD_TYPE.force)) {
1190                 Result<NsDAO.Data> ucr = q.mayUser(trans, user, role,
1191                         Access.write);
1192                 if (ucr.notOK()) {
1193                     return Result
1194                             .err(Status.ERR_Denied,
1195                                     "Role [%s.%s] does not exist. User [%s] cannot create.",
1196                                     role.ns, role.name, user);
1197                 }
1198
1199                 role.perms(true).add(pd.encode());
1200                 Result<RoleDAO.Data> rdd = q.roleDAO.create(trans, role);
1201                 if (rdd.isOK()) {
1202                     rv = Result.ok();
1203                 } else {
1204                     rv = Result.err(rdd);
1205                 }
1206             } else {
1207                 return Result.err(Status.ERR_RoleNotFound,
1208                         "Role [%s.%s] does not exist.", role.ns, role.name);
1209             }
1210         } else {
1211             role = rlrd.value.get(0);
1212             if (role.perms(false).contains(pd.encode())) {
1213                 return Result.err(Status.ERR_ConflictAlreadyExists,
1214                                 "Permission [%s.%s] is already a member of role [%s,%s]",
1215                                 pd.ns, pd.type, role.ns, role.name);
1216             }
1217             role.perms(true).add(pd.encode()); // this is added for Caching
1218                                                 // access purposes... doesn't
1219                                                 // affect addPerm
1220             rv = q.roleDAO.addPerm(trans, role, pd);
1221         }
1222         if (rv.status == Status.OK) {
1223             return q.permDAO.addRole(trans, pd, role);
1224             // exploring how to add information message to successful http
1225             // request
1226         }
1227         return rv;
1228     }
1229
1230     /**
1231      * Either Owner of Role or Permission may delete from Role
1232      * 
1233      * @param trans
1234      * @param role
1235      * @param pd
1236      * @return
1237      */
1238     public Result<Void> delPermFromRole(AuthzTrans trans, RoleDAO.Data role,PermDAO.Data pd, boolean fromApproval) {
1239         String user = trans.user();
1240         if (!fromApproval) {
1241             Result<NsDAO.Data> ucr = q.mayUser(trans, user, role, Access.write);
1242             Result<NsDAO.Data> ucp = q.mayUser(trans, user, pd, Access.write);
1243
1244             // If Can't change either Role or Perm, then deny
1245             if (ucr.notOK() && ucp.notOK()) {
1246                 return Result.err(Status.ERR_Denied,
1247                         "User [" + trans.user()
1248                                 + "] does not have permission to delete ["
1249                                 + pd.encode() + "] from Role ["
1250                                 + role.fullName() + ']');
1251             }
1252         }
1253
1254         Result<List<RoleDAO.Data>> rlr = q.roleDAO.read(trans, role);
1255         if (rlr.notOKorIsEmpty()) {
1256             // If Bad Data, clean out
1257             Result<List<PermDAO.Data>> rlp = q.permDAO.read(trans, pd);
1258             if (rlp.isOKhasData()) {
1259                 for (PermDAO.Data pv : rlp.value) {
1260                     q.permDAO.delRole(trans, pv, role);
1261                 }
1262             }
1263             return Result.err(rlr);
1264         }
1265         String perm1 = pd.encode();
1266         boolean notFound;
1267         if (trans.requested(REQD_TYPE.force)) {
1268             notFound = false;
1269         } else { // only check if force not set.
1270             notFound = true;
1271             for (RoleDAO.Data r : rlr.value) {
1272                 if (r.perms != null) {
1273                     for (String perm : r.perms) {
1274                         if (perm1.equals(perm)) {
1275                             notFound = false;
1276                             break;
1277                         }
1278                     }
1279                     if (!notFound) {
1280                         break;
1281                     }
1282                 }
1283             }
1284         }
1285         if (notFound) { // Need to check both, in case of corruption
1286             return Result.err(Status.ERR_PermissionNotFound,
1287                     "Permission [%s.%s|%s|%s] not associated with any Role",
1288                     pd.ns,pd.type,pd.instance,pd.action);
1289         }
1290
1291         // Read Perm for full data
1292         Result<List<PermDAO.Data>> rlp = q.permDAO.read(trans, pd);
1293         Result<Void> rv = null;
1294         if (rlp.isOKhasData()) {
1295             for (PermDAO.Data pv : rlp.value) {
1296                 if ((rv = q.permDAO.delRole(trans, pv, role)).isOK()) {
1297                     if ((rv = q.roleDAO.delPerm(trans, role, pv)).notOK()) {
1298                         trans.error().log(
1299                                 "Error removing Perm during delFromPermRole:",
1300                                 trans.getUserPrincipal(), rv.errorString());
1301                     }
1302                 } else {
1303                     trans.error().log(
1304                             "Error removing Role during delFromPermRole:",
1305                             trans.getUserPrincipal(), rv.errorString());
1306                 }
1307             }
1308         } else {
1309             rv = q.roleDAO.delPerm(trans, role, pd);
1310             if (rv.notOK()) {
1311                 trans.error().log("Error removing Role during delFromPermRole",
1312                         rv.errorString());
1313             }
1314         }
1315         return rv == null ? Result.ok() : rv;
1316     }
1317
1318     public Result<Void> delPermFromRole(AuthzTrans trans, String role,PermDAO.Data pd) {
1319         Result<NsSplit> nss = q.deriveNsSplit(trans, role);
1320         if (nss.notOK()) {
1321             return Result.err(nss);
1322         }
1323         RoleDAO.Data rd = new RoleDAO.Data();
1324         rd.ns = nss.value.ns;
1325         rd.name = nss.value.name;
1326         return delPermFromRole(trans, rd, pd, false);
1327     }
1328
1329     /**
1330      * Add a User to Role
1331      * 
1332      * 1) Role must exist 2) User must be a known Credential (i.e. mechID ok if
1333      * Credential) or known Organizational User
1334      * 
1335      * @param trans
1336      * @param org
1337      * @param urData
1338      * @return
1339      * @throws DAOException
1340      */
1341     public Result<Void> addUserRole(AuthzTrans trans,UserRoleDAO.Data urData) {
1342         Result<Void> rv;
1343         if (Question.ADMIN.equals(urData.rname)) {
1344             rv = mayAddAdmin(trans, urData.ns, urData.user);
1345         } else if (Question.OWNER.equals(urData.rname)) {
1346             rv = mayAddOwner(trans, urData.ns, urData.user);
1347         } else {
1348             rv = checkValidID(trans, new Date(), urData.user);
1349         }
1350         if (rv.notOK()) {
1351             return rv; 
1352         }
1353         
1354         // Check if record exists
1355         if (q.userRoleDAO.read(trans, urData).isOKhasData()) {
1356             return Result.err(Status.ERR_ConflictAlreadyExists,
1357                     "User Role exists");
1358         }
1359         if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) {
1360             return Result.err(Status.ERR_RoleNotFound,
1361                     "Role [%s.%s] does not exist", urData.ns, urData.rname);
1362         }
1363
1364         urData.expires = trans.org().expiration(null, Expiration.UserInRole, urData.user).getTime();
1365         
1366         
1367         Result<UserRoleDAO.Data> udr = q.userRoleDAO.create(trans, urData);
1368         switch (udr.status) {
1369         case OK:
1370             return Result.ok();
1371         default:
1372             return Result.err(udr);
1373         }
1374     }
1375
1376     public Result<Void> addUserRole(AuthzTrans trans, String user, String ns, String rname) {
1377         try {
1378             if (trans.org().getIdentity(trans, user)==null) {
1379                 return Result.err(Result.ERR_BadData,user+" is an Invalid Identity for " + trans.org().getName());
1380             }
1381         } catch (OrganizationException e) {
1382             return Result.err(e);
1383         }
1384         UserRoleDAO.Data urdd = new UserRoleDAO.Data();
1385         urdd.ns = ns;
1386         urdd.role(ns, rname);
1387         urdd.user = user;
1388         return addUserRole(trans,urdd);
1389     }
1390
1391     /**
1392      * Extend User Role.
1393      * 
1394      * extend the Expiration data, according to Organization rules.
1395      * 
1396      * @param trans
1397      * @param org
1398      * @param urData
1399      * @return
1400      */
1401     public Result<Void> extendUserRole(AuthzTrans trans, UserRoleDAO.Data urData, boolean checkForExist) {
1402         // Check if record still exists
1403         if (checkForExist && q.userRoleDAO.read(trans, urData).notOKorIsEmpty()) {
1404             return Result.err(Status.ERR_UserRoleNotFound,
1405                     "User Role does not exist");
1406         }
1407         
1408         if (q.roleDAO.read(trans, urData.ns, urData.rname).notOKorIsEmpty()) {
1409             return Result.err(Status.ERR_RoleNotFound,
1410                     "Role [%s.%s] does not exist", urData.ns,urData.rname);
1411         }
1412         // Special case for "Admin" roles. Issue brought forward with Prod
1413         // problem 9/26
1414         Date now = new Date();
1415         GregorianCalendar gc = new GregorianCalendar();
1416         gc.setTime(now.after(urData.expires)?now:urData.expires);
1417         urData.expires = trans.org().expiration(gc, Expiration.UserInRole).getTime(); // get
1418                                                                                 // Full
1419                                                                                 // time
1420                                                                                 // starting
1421                                                                                 // today
1422         return q.userRoleDAO.update(trans, urData);
1423     }
1424
1425     // ////////////////////////////////////////////////////
1426     // Special User Role Functions
1427     // These exist, because User Roles have Expiration dates, which must be
1428     // accounted for
1429     // Also, as of July, 2015, Namespace Owners and Admins are now regular User
1430     // Roles
1431     // ////////////////////////////////////////////////////
1432     public Result<List<String>> getUsersByRole(AuthzTrans trans, String role, boolean includeExpired) {
1433         Result<List<UserRoleDAO.Data>> rurdd = q.userRoleDAO.readByRole(trans,role);
1434         if (rurdd.notOK()) {
1435             return Result.err(rurdd);
1436         }
1437         Date now = new Date();
1438         List<UserRoleDAO.Data> list = rurdd.value;
1439         List<String> rv = new ArrayList<>(list.size()); // presize
1440         for (UserRoleDAO.Data urdd : rurdd.value) {
1441             if (includeExpired || urdd.expires.after(now)) {
1442                 rv.add(urdd.user);
1443             }
1444         }
1445         return Result.ok(rv);
1446     }
1447
1448     public Result<Void> delUserRole(AuthzTrans trans, String user, String ns, String rname) {
1449         UserRoleDAO.Data urdd = new UserRoleDAO.Data();
1450         urdd.user = user;
1451         urdd.role(ns,rname);
1452         Result<List<UserRoleDAO.Data>> r = q.userRoleDAO.read(trans, urdd);
1453         if (r.status == 404 || r.isEmpty()) {
1454             return Result.err(Status.ERR_UserRoleNotFound,
1455                     "UserRole [%s] [%s.%s]", user, ns, rname);
1456         }
1457         if (r.notOK()) {
1458             return Result.err(r);
1459         }
1460
1461         return q.userRoleDAO.delete(trans, urdd, false);
1462     }
1463
1464     public Result<String> createFuture(AuthzTrans trans, FutureDAO.Data data, String id, String user,
1465             NsDAO.Data nsd, FUTURE_OP op) {
1466         StringBuilder sb = new StringBuilder();
1467         try {
1468             Organization org = trans.org();
1469             // For Reapproval, only check Owners.. Do Supervisors, etc, separately
1470             List<Identity> approvers = op.equals(FUTURE_OP.A)?NO_ADDL_APPROVE:org.getApprovers(trans, user);
1471             List<Identity> owners = new ArrayList<>();
1472             if (nsd != null) {
1473                 Result<List<UserRoleDAO.Data>> rrbr = q.userRoleDAO
1474                         .readByRole(trans, nsd.name + Question.DOT_OWNER);
1475                 if (rrbr.isOKhasData()) {
1476                     for (UserRoleDAO.Data urd : rrbr.value) {
1477                         Identity owner = org.getIdentity(trans, urd.user);
1478                         if (owner==null) {
1479                             return Result.err(Result.ERR_NotFound,urd.user + " is not a Valid Owner of " + nsd.name);
1480                         } else {
1481                             owners.add(owner);
1482                         }
1483                     }
1484                 }
1485             }
1486             
1487             if (owners.isEmpty()) {
1488                 return Result.err(Result.ERR_NotFound,"No Owners found for " + nsd.name);
1489             }
1490             
1491             // Create Future Object
1492             
1493             Result<FutureDAO.Data> fr = q.futureDAO.create(trans, data, id);
1494             if (fr.isOK()) {
1495                 sb.append("Created Future: ");
1496                 sb.append(data.id);
1497                 // User Future ID as ticket for Approvals
1498                 final UUID ticket = fr.value.id;
1499                 sb.append(", Approvals: ");
1500                 Boolean first[] = new Boolean[]{true};
1501                 if (op!=FUTURE_OP.A) {
1502                     for (Identity u : approvers) {
1503                         Result<ApprovalDAO.Data> r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,org.getApproverType());
1504                         if (r.notOK()) {
1505                             return Result.err(r);
1506                         }
1507                     }
1508                 }
1509                 for (Identity u : owners) {
1510                     Result<ApprovalDAO.Data> r = addIdentity(trans,sb,first,user,data.memo,op,u,ticket,"owner");
1511                     if (r.notOK()) {
1512                         return Result.err(r);
1513                     }
1514                 }
1515             }
1516         } catch (Exception e) {
1517             return Result.err(e);
1518         }
1519         
1520         return Result.ok(sb.toString());
1521     }
1522
1523     /*
1524      * This interface is to allow performFutureOps with either Realtime Data, or Batched lookups (See Expiring)
1525      */
1526     public interface Lookup<T> {
1527         T get(AuthzTrans trans, Object ... keys);
1528     }
1529     
1530     public Lookup<UserRoleDAO.Data> urDBLookup = new Lookup<UserRoleDAO.Data>() {
1531         @Override
1532         public UserRoleDAO.Data get(AuthzTrans trans, Object ... keys) {
1533             Result<List<UserRoleDAO.Data>> r = q.userRoleDAO.read(trans, keys);
1534             if (r.isOKhasData()) {
1535                 return r.value.get(0);
1536             } else {
1537                 return null;
1538             }
1539         }
1540     };
1541
1542     /**
1543      * Note: if "allApprovals for Ticket is null, it will be looked up.  
1544      *       if "fdd" is null, it will be looked up, but
1545      *       
1546      * They can be passed for performance reasons.
1547      * 
1548      * @param trans
1549      * @param cd
1550      * @param allApprovalsForTicket
1551      * @return
1552      */
1553     public Result<OP_STATUS> performFutureOp(final AuthzTrans trans, FUTURE_OP fop, FutureDAO.Data curr, Lookup<List<ApprovalDAO.Data>> la, Lookup<UserRoleDAO.Data> lur) {
1554         // Pre-Evaluate if ReApproval is already done.
1555         UserRoleDAO.Data urdd = null;
1556         if (fop.equals(FUTURE_OP.A) && curr.target.equals(FOP_USER_ROLE) && curr.construct!=null) {
1557             try {
1558                 // Get Expected UserRole from Future
1559                 urdd = new UserRoleDAO.Data();
1560                 urdd.reconstitute(curr.construct);
1561                 // Get Current UserRole from lookup
1562                 UserRoleDAO.Data lurdd = lur.get(trans, urdd.user,urdd.role);
1563                 if (lurdd==null) {
1564                     q.futureDAO.delete(trans, curr, false);
1565                     return OP_STATUS.RL;
1566                 } else {
1567                     if (curr.expires.compareTo(lurdd.expires)<0) {
1568                         q.futureDAO.delete(trans, curr, false);
1569                         return OP_STATUS.RL;
1570                     }
1571                 }
1572             } catch (IOException e) {
1573                 return Result.err(Result.ERR_BadData,"Cannot reconstitute %1",curr.memo);
1574             }
1575         }
1576         
1577         boolean aDenial = false;
1578         int cntSuper=0, appSuper=0,cntOwner=0, appOwner=0;
1579         for (ApprovalDAO.Data add : la.get(trans)) {
1580             switch(add.status) {
1581                 case "approved":
1582                     if ("owner".equals(add.type)) {
1583                         ++cntOwner;
1584                         ++appOwner;
1585                     } else if ("supervisor".equals(add.type)) {
1586                         ++cntSuper;
1587                         ++appSuper;
1588                     }
1589                     break;
1590                 case "pending":
1591                     if ("owner".equals(add.type)) {
1592                         ++cntOwner;
1593                     } else if ("supervisor".equals(add.type)) {
1594                         ++cntSuper;
1595                     }
1596                     break;
1597                 case "denied":
1598                     aDenial=true;
1599                     break;
1600             }
1601         }
1602         
1603         Result<OP_STATUS> ros=null;
1604         if (aDenial) {
1605             // Note: Denial will be Audit-logged.
1606 //            for (ApprovalDAO.Data ad : allApprovalsForTicket.value) {
1607 //                q.approvalDAO.delete(trans, ad, false);
1608 //            }
1609             ros = OP_STATUS.RD;
1610             if (q.futureDAO.delete(trans, curr, false).notOK()) {
1611                 trans.info().printf("Future %s could not be deleted", curr.id.toString());
1612             }  else {
1613                 if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) {
1614                     // A Denial means we must remove UserRole
1615                     if (fop.equals(FUTURE_OP.U) || fop.equals(FUTURE_OP.A)) {
1616                         UserRoleDAO.Data data = new UserRoleDAO.Data();
1617                         try {
1618                             data.reconstitute(curr.construct);
1619                         } catch (IOException e) {
1620                             trans.error().log("Cannot reconstitue",curr.memo);
1621                         }
1622                         ros = set(OP_STATUS.RD,delUserRole(trans, data.user, data.ns, data.rname));
1623                     }
1624                 }
1625             }
1626         }
1627         
1628         // Decision: If not Denied, and at least owner, if exists, and at least one Super, if exists
1629         boolean goDecision = (cntOwner>0?appOwner>0:true) && (cntSuper>0?appSuper>0:true);
1630
1631         if (goDecision) {
1632             // should check if any other pendings before performing
1633             // actions
1634             try {
1635                 if (FOP_ROLE.equalsIgnoreCase(curr.target)) {
1636                     RoleDAO.Data data = new RoleDAO.Data();
1637                     data.reconstitute(curr.construct);
1638                     switch(fop) {
1639                         case C:
1640                             ros = set(OP_STATUS.RE,q.roleDAO.dao().create(trans, data));
1641                             break;
1642                         case D:
1643                             ros = set(OP_STATUS.RE,deleteRole(trans, data, true, true));
1644                             break;
1645                         default:
1646                     }
1647                 } else if (FOP_PERM.equalsIgnoreCase(curr.target)) {
1648                     PermDAO.Data pdd = new PermDAO.Data();
1649                     pdd.reconstitute(curr.construct);
1650                     Set<String> roles;
1651                     Result<RoleDAO.Data> rrdd;
1652                     switch(fop) {
1653                         case C:
1654                             ros = set(OP_STATUS.RE,createPerm(trans, pdd, true));
1655                             break;
1656                         case D:
1657                             ros = set(OP_STATUS.RE,deletePerm(trans, pdd, true, true));
1658                             break;
1659                         case G:
1660                             roles = pdd.roles(true);
1661                             for (String roleStr : roles) {
1662                                 rrdd = RoleDAO.Data.decode(trans, q, roleStr);
1663                                 if (rrdd.isOKhasData()) {
1664                                     ros = set(OP_STATUS.RE,addPermToRole(trans, rrdd.value, pdd, true));
1665                                 } else {
1666                                     trans.error().log(rrdd.errorString());
1667                                 }
1668                             }
1669                             break;
1670                         case UG:
1671                             roles = pdd.roles(true);
1672                             for (String roleStr : roles) {
1673                                 rrdd = RoleDAO.Data.decode(trans, q, roleStr);
1674                                 if (rrdd.isOKhasData()) {
1675                                     ros = set(OP_STATUS.RE,delPermFromRole(trans, rrdd.value, pdd,    true));
1676                                 } else {
1677                                     trans.error().log(rrdd.errorString());
1678                                 }
1679                             }
1680                             break;
1681                         default:
1682                     }
1683                 } else if (FOP_USER_ROLE.equalsIgnoreCase(curr.target)) {
1684                     if (urdd==null) {
1685                         urdd = new UserRoleDAO.Data();
1686                         urdd.reconstitute(curr.construct);
1687                     }
1688                     // if I am the last to approve, create user role
1689                     switch(fop) {
1690                         case C:
1691                             ros = set(OP_STATUS.RE,addUserRole(trans, urdd));
1692                             break;
1693                         case U:
1694                         case A:
1695                             ros = set(OP_STATUS.RE,extendUserRole(trans,urdd,true));
1696                             break;
1697                         default:
1698                     }
1699                 } else if (FOP_NS.equalsIgnoreCase(curr.target)) {
1700                     Namespace namespace = new Namespace();
1701                     namespace.reconstitute(curr.construct);
1702                     switch(fop) {
1703                         case C:
1704                             ros = set(OP_STATUS.RE,createNS(trans, namespace, true));
1705                             break;
1706                         default:
1707                     }
1708                 } else if (FOP_DELEGATE.equalsIgnoreCase(curr.target)) {
1709                     DelegateDAO.Data data = new DelegateDAO.Data();
1710                     data.reconstitute(curr.construct);
1711                     switch(fop) {
1712                         case C:
1713                             ros = set(OP_STATUS.RE,q.delegateDAO.create(trans, data));
1714                             break;
1715                         case U:
1716                             ros = set(OP_STATUS.RE,q.delegateDAO.update(trans, data));
1717                             break;
1718                         default:
1719                     }
1720                 } else if (FOP_CRED.equalsIgnoreCase(curr.target)) {
1721                     CredDAO.Data data = new CredDAO.Data();
1722                     data.reconstitute(curr.construct);
1723                     switch(fop) {
1724                         case C:
1725                             ros = set(OP_STATUS.RE,q.credDAO.dao().create(trans, data));
1726                             break;
1727                         default:
1728                     }
1729                 }                
1730             } catch (Exception e) {
1731                 trans.error().log("Exception: ", e.getMessage(),
1732                     " \n occurred while performing", curr.memo,
1733                     " from Ticket ", curr.id.toString());
1734             }
1735             q.futureDAO.delete(trans, curr, false);
1736         } // end for goDecision
1737         if (ros==null) {
1738             //return Result.err(Status.ACC_Future, "Full Approvals not obtained: No action taken");
1739             ros = OP_STATUS.RP;
1740         }
1741             
1742         return ros;
1743     }
1744
1745     // Convenience method for setting OPSTatus Results
1746     private Result<OP_STATUS> set(Result<OP_STATUS> rs, Result<?> orig) {
1747         if (orig.isOK()) {
1748             return rs;
1749         } else {
1750             return Result.err(orig);
1751         }
1752     }
1753
1754     private Result<ApprovalDAO.Data>  addIdentity(AuthzTrans trans, StringBuilder sb, 
1755                         Boolean[] first, String user, String memo, FUTURE_OP op, Identity u, UUID ticket, String type) throws OrganizationException {
1756         ApprovalDAO.Data ad = new ApprovalDAO.Data();
1757         // Note ad.id is set by ApprovalDAO Create
1758         ad.ticket = ticket;
1759         ad.user = user;
1760         ad.approver = u.fullID();
1761         ad.status = ApprovalDAO.PENDING;
1762         ad.memo = memo;
1763         ad.type = type;
1764         ad.operation = op.name();
1765         // Note ad.updated is created in System
1766         Result<ApprovalDAO.Data> r = q.approvalDAO.create(trans,ad);
1767         if (r.isOK()) {
1768             if (first[0]) {
1769                 first[0] = false;
1770             } else {
1771                 sb.append(", ");
1772             }
1773             sb.append(r.value.user);
1774             sb.append(':');
1775             sb.append(r.value.ticket);
1776             return r;
1777         } else {
1778             return Result.err(Status.ERR_ActionNotCompleted,
1779                     "Approval for %s, %s could not be created: %s",
1780                     ad.user, ad.approver,
1781                     r.details, sb.toString());
1782         }
1783     }
1784
1785     public Executor newExecutor(AuthzTrans trans) {
1786         return new CassExecutor(trans, this);
1787     }
1788
1789 }