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