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