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