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