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