Adjust Agent for none K8s
[aaf/authz.git] / auth / auth-service / src / main / java / org / onap / aaf / auth / service / AuthzCassServiceImpl.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.service;
23
24 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.force;
25 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.future;
26 import static org.onap.aaf.auth.layer.Result.OK;
27 import static org.onap.aaf.auth.rserv.HttpMethods.DELETE;
28 import static org.onap.aaf.auth.rserv.HttpMethods.GET;
29 import static org.onap.aaf.auth.rserv.HttpMethods.POST;
30 import static org.onap.aaf.auth.rserv.HttpMethods.PUT;
31
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Date;
37 import java.util.GregorianCalendar;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.Set;
43 import java.util.TreeMap;
44 import java.util.UUID;
45
46 import javax.servlet.http.HttpServletRequest;
47
48 import org.onap.aaf.auth.common.Define;
49 import org.onap.aaf.auth.dao.DAOException;
50 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
51 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
52 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
53 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
54 import org.onap.aaf.auth.dao.cass.CertDAO;
55 import org.onap.aaf.auth.dao.cass.CredDAO;
56 import org.onap.aaf.auth.dao.cass.DelegateDAO;
57 import org.onap.aaf.auth.dao.cass.FutureDAO;
58 import org.onap.aaf.auth.dao.cass.HistoryDAO;
59 import org.onap.aaf.auth.dao.cass.Namespace;
60 import org.onap.aaf.auth.dao.cass.NsDAO;
61 import org.onap.aaf.auth.dao.cass.NsDAO.Data;
62 import org.onap.aaf.auth.dao.cass.NsSplit;
63 import org.onap.aaf.auth.dao.cass.NsType;
64 import org.onap.aaf.auth.dao.cass.PermDAO;
65 import org.onap.aaf.auth.dao.cass.RoleDAO;
66 import org.onap.aaf.auth.dao.cass.Status;
67 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
68 import org.onap.aaf.auth.dao.hl.CassExecutor;
69 import org.onap.aaf.auth.dao.hl.Function;
70 import org.onap.aaf.auth.dao.hl.Function.FUTURE_OP;
71 import org.onap.aaf.auth.dao.hl.Function.Lookup;
72 import org.onap.aaf.auth.dao.hl.Function.OP_STATUS;
73 import org.onap.aaf.auth.dao.hl.PermLookup;
74 import org.onap.aaf.auth.dao.hl.Question;
75 import org.onap.aaf.auth.dao.hl.Question.Access;
76 import org.onap.aaf.auth.env.AuthzTrans;
77 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
78 import org.onap.aaf.auth.layer.Result;
79 import org.onap.aaf.auth.org.Executor;
80 import org.onap.aaf.auth.org.Organization;
81 import org.onap.aaf.auth.org.Organization.Expiration;
82 import org.onap.aaf.auth.org.Organization.Identity;
83 import org.onap.aaf.auth.org.Organization.Policy;
84 import org.onap.aaf.auth.org.OrganizationException;
85 import org.onap.aaf.auth.rserv.doc.ApiDoc;
86 import org.onap.aaf.auth.service.mapper.Mapper;
87 import org.onap.aaf.auth.service.mapper.Mapper.API;
88 import org.onap.aaf.auth.service.validation.ServiceValidator;
89 import org.onap.aaf.auth.validation.Validator;
90 import org.onap.aaf.cadi.aaf.Defaults;
91 import org.onap.aaf.cadi.principal.BasicPrincipal;
92 import org.onap.aaf.cadi.util.FQI;
93 import org.onap.aaf.misc.env.Env;
94 import org.onap.aaf.misc.env.TimeTaken;
95 import org.onap.aaf.misc.env.util.Chrono;
96 import org.onap.aaf.misc.env.util.Split;
97
98 import aaf.v2_0.CredRequest;
99
100 /**
101  * AuthzCassServiceImpl implements AuthzCassService for 
102  * 
103  * @author Jonathan
104  *
105  * @param <NSS>
106  * @param <PERMS>
107  * @param <PERMKEY>
108  * @param <ROLES>
109  * @param <USERS>
110  * @param <DELGS>
111  * @param <REQUEST>
112  * @param <HISTORY>
113  * @param <ERR>
114  * @param <APPROVALS>
115  */
116 public class AuthzCassServiceImpl    <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS>
117     implements AuthzService            <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> {
118     
119     private static final String TWO_SPACE = "  ";
120         private Mapper                    <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper;
121     @Override
122     public Mapper                    <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper() {return mapper;}
123     
124     private static final String ASTERIX = "*";
125     private static final String CACHE = "cache";
126     private static final String ROOT_NS = Define.ROOT_NS();
127     private static final String ROOT_COMPANY = Define.ROOT_COMPANY();
128
129     private final Question ques;
130     private final Function func;
131     
132     public AuthzCassServiceImpl(AuthzTrans trans, Mapper<NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper,Question question) {
133         this.ques = question;
134         func = new Function(trans, question);
135         this.mapper = mapper;
136         
137     }
138
139 /***********************************
140  * NAMESPACE 
141  ***********************************/
142     /**
143      * createNS
144      * @throws DAOException 
145      * @see org.onap.aaf.auth.service.AuthzService#createNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
146      */
147     @ApiDoc( 
148             method = POST,  
149             path = "/authz/ns",
150             params = {},
151             expectedCode = 201,
152             errorCodes = { 403,404,406,409 }, 
153             text = { "Namespace consists of: ",
154                     "<ul><li>name - What you want to call this Namespace</li>",
155                     "<li>responsible(s) - Person(s) who receive Notifications and approves Requests ",
156                     "regarding this Namespace. Companies have Policies as to who may take on ",
157                     "this Responsibility. Separate multiple identities with commas</li>",
158                     "<li>admin(s) - Person(s) who are allowed to make changes on the namespace, ",
159                     "including creating Roles, Permissions and Credentials. Separate multiple ",
160                     "identities with commas</li></ul>",
161                     "Note: Namespaces are dot-delimited (i.e. com.myCompany.myApp) and must be ",
162                     "created with parent credentials (i.e. To create com.myCompany.myApp, you must ",
163                     "be an admin of com.myCompany or com"
164                     }
165             )
166     @Override
167     public Result<Void> createNS(final AuthzTrans trans, REQUEST from, NsType type) {
168         final Result<Namespace> rnamespace = mapper.ns(trans, from);
169         final ServiceValidator v = new ServiceValidator();
170         if (v.ns(rnamespace).err()) { 
171             return Result.err(Status.ERR_BadData,v.errs());
172         }
173         final Namespace namespace = rnamespace.value;
174         final Result<NsDAO.Data> parentNs = ques.deriveNs(trans,namespace.name);
175         if (parentNs.notOK()) {
176             return Result.err(parentNs);
177         }
178         
179         // Note: Data validate occurs in func.createNS
180         if (namespace.name.lastIndexOf('.')<0) { // Root Namespace... Function will check if allowed
181             return func.createNS(trans, namespace, false);
182         }
183         
184         Result<FutureDAO.Data> fd = mapper.future(trans, NsDAO.TABLE,from,namespace,true, 
185                 new Mapper.Memo() {
186                     @Override
187                     public String get() {
188                         return "Create Namespace [" + namespace.name + ']';
189                     }
190                 },
191                 new MayChange() {
192                     private Result<NsDAO.Data> rnd;
193                     @Override
194                     public Result<?> mayChange() {
195                         if (rnd==null) {
196                             rnd = ques.mayUser(trans, trans.user(), parentNs.value,Access.write);
197                         }
198                         return rnd;
199                     }
200                 });
201             switch(fd.status) {
202                 case OK:
203                     Result<String> rfc = func.createFuture(trans, fd.value, namespace.name, trans.user(),parentNs.value, FUTURE_OP.C);
204                     if (rfc.isOK()) {
205                         return Result.err(Status.ACC_Future, "NS [%s] is saved for future processing",namespace.name);
206                     } else { 
207                         return Result.err(rfc);
208                     }
209                 case Status.ACC_Now:
210                     return func.createNS(trans, namespace, false);
211                 default:
212                     return Result.err(fd);
213             }
214     }
215     
216     @ApiDoc(
217             method = POST,  
218             path = "/authz/ns/:ns/admin/:id",
219             params = {     "ns|string|true",
220                         "id|string|true" 
221                     },
222             expectedCode = 201,
223             errorCodes = { 403,404,406,409 }, 
224             text = {     "Add an Identity :id to the list of Admins for the Namespace :ns", 
225                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
226             )
227     @Override
228     public Result<Void> addAdminNS(AuthzTrans trans, String ns, String id) {
229         return func.addUserRole(trans, id, ns,Question.ADMIN);
230     }
231
232     @ApiDoc(
233             method = DELETE,  
234             path = "/authz/ns/:ns/admin/:id",
235             params = {     "ns|string|true",
236                         "id|string|true" 
237                     },
238             expectedCode = 200,
239             errorCodes = { 403,404 }, 
240             text = {     "Remove an Identity :id from the list of Admins for the Namespace :ns",
241                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
242             )
243     @Override
244     public Result<Void> delAdminNS(AuthzTrans trans, String ns, String id) {
245         return func.delAdmin(trans,ns,id);
246     }
247
248     @ApiDoc(
249             method = POST,  
250             path = "/authz/ns/:ns/responsible/:id",
251             params = {     "ns|string|true",
252                         "id|string|true" 
253                     },
254             expectedCode = 201,
255             errorCodes = { 403,404,406,409 }, 
256             text = {     "Add an Identity :id to the list of Responsibles for the Namespace :ns",
257                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
258             )
259     @Override
260     public Result<Void> addResponsibleNS(AuthzTrans trans, String ns, String id) {
261         return func.addUserRole(trans,id,ns,Question.OWNER);
262     }
263
264     @ApiDoc(
265             method = DELETE,  
266             path = "/authz/ns/:ns/responsible/:id",
267             params = {     "ns|string|true",
268                         "id|string|true" 
269                     },
270             expectedCode = 200,
271             errorCodes = { 403,404 }, 
272             text = {     "Remove an Identity :id to the list of Responsibles for the Namespace :ns",
273                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)",
274                         "Note: A namespace must have at least 1 responsible party"
275                     }
276             )
277     @Override
278     public Result<Void> delResponsibleNS(AuthzTrans trans, String ns, String id) {
279         return func.delOwner(trans,ns,id);
280     }
281
282     /* (non-Javadoc)
283      * @see org.onap.aaf.auth.service.AuthzService#applyModel(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
284      */
285     @ApiDoc(
286             method = POST,  
287             path = "/authz/ns/:ns/attrib/:key/:value",
288             params = {     "ns|string|true",
289                         "key|string|true",
290                         "value|string|true"},
291             expectedCode = 201,
292             errorCodes = { 403,404,406,409 },  
293             text = {     
294                 "Create an attribute in the Namespace",
295                 "You must be given direct permission for key by AAF"
296                 }
297             )
298     @Override
299     public Result<Void> createNsAttrib(AuthzTrans trans, String ns, String key, String value) {
300         TimeTaken tt = trans.start("Create NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
301         try {
302             // Check inputs
303             final Validator v = new ServiceValidator();
304             if (v.ns(ns).err() ||
305                v.key(key).err() ||
306                v.value(value).err()) {
307                 return Result.err(Status.ERR_BadData,v.errs());
308             }
309
310             // Check if exists already
311             Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
312             if (rlnsd.notOKorIsEmpty()) {
313                 return Result.err(rlnsd);
314             }
315             NsDAO.Data nsd = rlnsd.value.get(0);
316
317             // Check for Existence
318             if (nsd.attrib.get(key)!=null) {
319                 return Result.err(Status.ERR_ConflictAlreadyExists, "NS Property %s:%s exists", ns, key);
320             }
321             
322             // Check if User may put
323             if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
324                     ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
325                 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
326             }
327
328             // Add Attrib
329             nsd.attrib.put(key, value);
330             ques.nsDAO().dao().attribAdd(trans,ns,key,value);
331             ques.nsDAO().invalidate(trans, nsd);
332             return Result.ok();
333         } finally {
334             tt.done();
335         }
336     }
337     
338     @ApiDoc(
339             method = GET,  
340             path = "/authz/ns/attrib/:key",
341             params = {     "key|string|true" },
342             expectedCode = 200,
343             errorCodes = { 403,404 },  
344             text = {     
345                 "Read Attributes for Namespace"
346                 }
347             )
348     @Override
349     public Result<KEYS> readNsByAttrib(AuthzTrans trans, String key) {
350         // Check inputs
351         final Validator v = new ServiceValidator();
352         if (v.nullOrBlank("Key",key).err()) {
353               return Result.err(Status.ERR_BadData,v.errs());
354         }
355
356         // May Read
357         if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
358                     ":"+trans.org().getDomain()+".*:"+key, Question.READ)) {
359             return Result.err(Status.ERR_Denied,"%s may not read NS by Attrib '%s'",trans.user(),key);
360         }
361
362         Result<Set<String>> rsd = ques.nsDAO().dao().readNsByAttrib(trans, key);
363         if (rsd.notOK()) {
364             return Result.err(rsd);
365         }
366         return mapper().keys(rsd.value);
367     }
368
369
370     @ApiDoc(
371             method = PUT,  
372             path = "/authz/ns/:ns/attrib/:key/:value",
373             params = {     "ns|string|true",
374                         "key|string|true"},
375             expectedCode = 200,
376             errorCodes = { 403,404 },  
377             text = {     
378                 "Update Value on an existing attribute in the Namespace",
379                 "You must be given direct permission for key by AAF"
380                 }
381             )
382     @Override
383     public Result<?> updateNsAttrib(AuthzTrans trans, String ns, String key, String value) {
384         TimeTaken tt = trans.start("Update NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
385         try {
386             // Check inputs
387             final Validator v = new ServiceValidator();
388             if (v.ns(ns).err() ||
389                v.key(key).err() ||
390                v.value(value).err()) {
391                 return Result.err(Status.ERR_BadData,v.errs());
392             }
393
394             // Check if exists already (NS must exist)
395             Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
396             if (rlnsd.notOKorIsEmpty()) {
397                 return Result.err(rlnsd);
398             }
399             NsDAO.Data nsd = rlnsd.value.get(0);
400
401             // Check for Existence
402             if (nsd.attrib.get(key)==null) {
403                 return Result.err(Status.ERR_NotFound, "NS Property %s:%s exists", ns, key);
404             }
405             
406             // Check if User may put
407             if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
408                     ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
409                 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
410             }
411
412             // Add Attrib
413             nsd.attrib.put(key, value);
414             ques.nsDAO().invalidate(trans, nsd);
415             return ques.nsDAO().update(trans,nsd);
416  
417         } finally {
418             tt.done();
419         }
420     }
421
422     @ApiDoc(
423             method = DELETE,  
424             path = "/authz/ns/:ns/attrib/:key",
425             params = {     "ns|string|true",
426                         "key|string|true"},
427             expectedCode = 200,
428             errorCodes = { 403,404 },  
429             text = {     
430                 "Delete an attribute in the Namespace",
431                 "You must be given direct permission for key by AAF"
432                 }
433             )
434     @Override
435     public Result<Void> deleteNsAttrib(AuthzTrans trans, String ns, String key) {
436         TimeTaken tt = trans.start("Delete NsAttrib " + ns + ':' + key, Env.SUB);
437         try {
438             // Check inputs
439             final Validator v = new ServiceValidator();
440             if (v.nullOrBlank("NS",ns).err() ||
441                v.nullOrBlank("Key",key).err()) {
442                 return Result.err(Status.ERR_BadData,v.errs());
443             }
444
445             // Check if exists already
446             Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
447             if (rlnsd.notOKorIsEmpty()) {
448                 return Result.err(rlnsd);
449             }
450             NsDAO.Data nsd = rlnsd.value.get(0);
451
452             // Check for Existence
453             if (nsd.attrib.get(key)==null) {
454                 return Result.err(Status.ERR_NotFound, "NS Property [%s:%s] does not exist", ns, key);
455             }
456             
457             // Check if User may del
458             if (!ques.isGranted(trans, trans.user(), ROOT_NS, "attrib", ":" + ROOT_COMPANY + ".*:"+key, Access.write.name())) {
459                 return Result.err(Status.ERR_Denied, "%s may not delete NS Attrib [%s:%s]", trans.user(),ns, key);
460             }
461
462             // Add Attrib
463             nsd.attrib.remove(key);
464             ques.nsDAO().dao().attribRemove(trans,ns,key);
465             ques.nsDAO().invalidate(trans, nsd);
466             return Result.ok();
467         } finally {
468             tt.done();
469         }
470     }
471
472     @ApiDoc(
473             method = GET,  
474             path = "/authz/nss/:id",
475             params = {     "id|string|true" },
476             expectedCode = 200,
477             errorCodes = { 404,406 }, 
478             text = {     
479                 "Lists the Owner(s), Admin(s), Description, and Attributes of Namespace :id",
480             }
481             )
482     @Override
483     public Result<NSS> getNSbyName(AuthzTrans trans, String ns, boolean includeExpired) {
484         final Validator v = new ServiceValidator();
485         if (v.nullOrBlank("NS", ns).err()) {
486             return Result.err(Status.ERR_BadData,v.errs());
487         }
488         
489         Result<List<NsDAO.Data>> rlnd = ques.nsDAO().read(trans, ns);
490         if (rlnd.isOK()) {
491             if (rlnd.isEmpty()) {
492                 return Result.err(Status.ERR_NotFound, "No data found for %s",ns);
493             }
494             Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.read);
495             if (rnd.notOK()) {
496                 return Result.err(rnd); 
497             }
498             
499             
500             Namespace namespace = new Namespace(rnd.value);
501             Result<List<String>> rd = func.getOwners(trans, namespace.name, includeExpired);
502             if (rd.isOK()) {
503                 namespace.owner = rd.value;
504             }
505             rd = func.getAdmins(trans, namespace.name, includeExpired);
506             if (rd.isOK()) {
507                 namespace.admin = rd.value;
508             }
509             
510             NSS nss = mapper.newInstance(API.NSS);
511             return mapper.nss(trans, namespace, nss);
512         } else {
513             return Result.err(rlnd);
514         }
515     }
516
517     @ApiDoc(
518             method = GET,  
519             path = "/authz/nss/admin/:id",
520             params = {     "id|string|true" },
521             expectedCode = 200,
522             errorCodes = { 403,404 }, 
523             text = {     "Lists all Namespaces where Identity :id is an Admin", 
524                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" 
525                     }
526             )
527     @Override
528     public Result<NSS> getNSbyAdmin(AuthzTrans trans, String user, boolean full) {
529         final Validator v = new ServiceValidator();
530         if (v.nullOrBlank("User", user).err()) {
531             return Result.err(Status.ERR_BadData, v.errs());
532         }
533         
534         Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".admin", full);
535         if (rn.notOK()) {
536             return Result.err(rn);
537         }
538         if (rn.isEmpty()) {
539             return Result.err(Status.ERR_NotFound, "[%s] is not an admin for any namespaces",user);        
540         }
541         NSS nss = mapper.newInstance(API.NSS);
542         // Note: "loadNamespace" already validates view of Namespace
543         return mapper.nss(trans, rn.value, nss);
544     }
545
546     @ApiDoc(
547             method = GET,  
548             path = "/authz/nss/either/:id",
549             params = {     "id|string|true" },
550             expectedCode = 200,
551             errorCodes = { 403,404 }, 
552             text = {     "Lists all Namespaces where Identity :id is either an Admin or an Owner", 
553                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" 
554                     }
555             )
556     @Override
557     public Result<NSS> getNSbyEither(AuthzTrans trans, String user, boolean full) {
558         final Validator v = new ServiceValidator();
559         if (v.nullOrBlank("User", user).err()) {
560             return Result.err(Status.ERR_BadData, v.errs());
561         }
562         
563         Result<Collection<Namespace>> rn = loadNamepace(trans, user, null, full);
564         if (rn.notOK()) {
565             return Result.err(rn);
566         }
567         if (rn.isEmpty()) {
568             return Result.err(Status.ERR_NotFound, "[%s] is not an admin or owner for any namespaces",user);        
569         }
570         NSS nss = mapper.newInstance(API.NSS);
571         // Note: "loadNamespace" already validates view of Namespace
572         return mapper.nss(trans, rn.value, nss);
573     }
574
575     private Result<Collection<Namespace>> loadNamepace(AuthzTrans trans, String user, String endsWith, boolean full) {
576         Result<List<UserRoleDAO.Data>> urd = ques.userRoleDAO().readByUser(trans, user);
577         if (urd.notOKorIsEmpty()) {
578             return Result.err(urd);
579         }
580         Map<String, Namespace> lm = new HashMap<>();
581         Map<String, Namespace> other = full || endsWith==null?null:new TreeMap<>();
582         for (UserRoleDAO.Data urdd : urd.value) {
583             if (full) {
584                 if (endsWith==null || urdd.role.endsWith(endsWith)) {
585                     RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
586                     Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
587                     if (nsd.isOK()) {
588                         Namespace namespace = lm.get(nsd.value.name);
589                         if (namespace==null) {
590                             namespace = new Namespace(nsd.value);
591                             lm.put(namespace.name,namespace);
592                         }
593                         Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
594                         if (rls.isOK()) {
595                             namespace.admin=rls.value;
596                         }
597                         
598                         rls = func.getOwners(trans, namespace.name, false);
599                         if (rls.isOK()) {
600                             namespace.owner=rls.value;
601                         }
602                     }
603                 }
604             } else { // Shortened version.  Only Namespace Info available from Role.
605                 if (Question.ADMIN.equals(urdd.rname) || Question.OWNER.equals(urdd.rname)) {
606                     RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
607                     Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
608                     if (nsd.isOK()) {
609                         Namespace namespace = lm.get(nsd.value.name);
610                         if (namespace==null) {
611                             if (other!=null) {
612                                 namespace = other.remove(nsd.value.name);
613                             }
614                             if (namespace==null) {
615                                 namespace = new Namespace(nsd.value);
616                                 namespace.admin=new ArrayList<>();
617                                 namespace.owner=new ArrayList<>();
618                             }
619                             if (endsWith==null || urdd.role.endsWith(endsWith)) {
620                                 lm.put(namespace.name,namespace);
621                             } else { 
622                                 other.put(namespace.name,namespace);
623                             }
624                         }
625                         if (Question.OWNER.equals(urdd.rname)) {
626                             namespace.owner.add(urdd.user);
627                         } else {
628                             namespace.admin.add(urdd.user);
629                         }
630                     }
631                 }
632             }
633         }
634         return Result.ok(lm.values());
635     }
636
637     @ApiDoc(
638             method = GET,  
639             path = "/authz/nss/responsible/:id",
640             params = {     "id|string|true" },
641             expectedCode = 200,
642             errorCodes = { 403,404 }, 
643             text = {     "Lists all Namespaces where Identity :id is a Responsible Party", 
644                         "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)"
645                     }
646             )
647     @Override
648     public Result<NSS> getNSbyResponsible(AuthzTrans trans, String user, boolean full) {
649         final Validator v = new ServiceValidator();
650         if (v.nullOrBlank("User", user).err()) {
651             return Result.err(Status.ERR_BadData, v.errs());
652         }
653         Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".owner",full);
654         if (rn.notOK()) {
655             return Result.err(rn);
656         }
657         if (rn.isEmpty()) {
658             return Result.err(Status.ERR_NotFound, "[%s] is not an owner for any namespaces",user);        
659         }
660         NSS nss = mapper.newInstance(API.NSS);
661         // Note: "loadNamespace" prevalidates
662         return mapper.nss(trans, rn.value, nss);
663     }
664     
665     @ApiDoc(
666             method = GET,  
667             path = "/authz/nss/children/:id",
668             params = {     "id|string|true" },
669             expectedCode = 200,
670             errorCodes = { 403,404 }, 
671             text = {     "Lists all Child Namespaces of Namespace :id", 
672                         "Note: This is not a cached read"
673                     }
674             )
675     @Override
676     public Result<NSS> getNSsChildren(AuthzTrans trans, String parent) {
677         final Validator v = new ServiceValidator();
678         if (v.nullOrBlank("NS", parent).err())  {
679             return Result.err(Status.ERR_BadData,v.errs());
680         }
681         
682         Result<NsDAO.Data> rnd = ques.deriveNs(trans, parent);
683         if (rnd.notOK()) {
684             return Result.err(rnd);
685         }
686         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
687         if (rnd.notOK()) {
688             return Result.err(rnd); 
689         }
690
691         Set<Namespace> lm = new HashSet<>();
692         Result<List<NsDAO.Data>> rlnd = ques.nsDAO().dao().getChildren(trans, parent);
693         if (rlnd.isOK()) {
694             if (rlnd.isEmpty()) {
695                 return Result.err(Status.ERR_NotFound, "No data found for %s",parent);
696             }
697             for (NsDAO.Data ndd : rlnd.value) {
698                 Namespace namespace = new Namespace(ndd);
699                 Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
700                 if (rls.isOK()) {
701                     namespace.admin=rls.value;
702                 }
703                 
704                 rls = func.getOwners(trans, namespace.name, false);
705                 if (rls.isOK()) {
706                     namespace.owner=rls.value;
707                 }
708
709                 lm.add(namespace);
710             }
711             NSS nss = mapper.newInstance(API.NSS);
712             return mapper.nss(trans,lm, nss);
713         } else {
714             return Result.err(rlnd);
715         }
716     }
717
718
719     @ApiDoc(
720             method = PUT,  
721             path = "/authz/ns",
722             params = {},
723             expectedCode = 200,
724             errorCodes = { 403,404,406 }, 
725             text = { "Replace the Current Description of a Namespace with a new one"
726                     }
727             )
728     @Override
729     public Result<Void> updateNsDescription(AuthzTrans trans, REQUEST from) {
730         final Result<Namespace> nsd = mapper.ns(trans, from);
731         final ServiceValidator v = new ServiceValidator();
732         if (v.ns(nsd).err()) {
733             return Result.err(Status.ERR_BadData,v.errs());
734         }
735         if (v.nullOrBlank("description", nsd.value.description).err()) {
736             return Result.err(Status.ERR_BadData,v.errs());
737         }
738
739         Namespace namespace = nsd.value;
740         Result<List<NsDAO.Data>> rlnd = ques.nsDAO().read(trans, namespace.name);
741         
742         if (rlnd.notOKorIsEmpty()) {
743             return Result.err(Status.ERR_NotFound, "Namespace [%s] does not exist",namespace.name);
744         }
745         
746         if (ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.write).notOK()) {
747             return Result.err(Status.ERR_Denied, "You do not have approval to change %s",namespace.name);
748         }
749
750         Result<Void> rdr = ques.nsDAO().dao().addDescription(trans, namespace.name, namespace.description);
751         if (rdr.isOK()) {
752             return Result.ok();
753         } else {
754             return Result.err(rdr);
755         }
756     }
757     
758     /**
759      * deleteNS
760      * @throws DAOException 
761      * @see org.onap.aaf.auth.service.AuthzService#deleteNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
762      */
763     @ApiDoc(
764             method = DELETE,  
765             path = "/authz/ns/:ns",
766             params = {     "ns|string|true" },
767             expectedCode = 200,
768             errorCodes = { 403,404,424 }, 
769             text = {     "Delete the Namespace :ns. Namespaces cannot normally be deleted when there ",
770                         "are still credentials associated with them, but they can be deleted by setting ",
771                         "the \"force\" property. To do this: Add 'force=true' as a query parameter",
772                         "<p>WARNING: Using force will delete all credentials attached to this namespace. Use with care.</p>"
773                         + "if the \"force\" property is set to 'force=move', then Permissions and Roles are not deleted,"
774                         + "but are retained, and assigned to the Parent Namespace.  'force=move' is not permitted "
775                         + "at or below Application Scope"
776                         }
777             )
778     @Override
779     public Result<Void> deleteNS(AuthzTrans trans, String ns) {
780         return func.deleteNS(trans, ns);
781     }
782
783
784 /***********************************
785  * PERM 
786  ***********************************/
787
788     /*
789      * (non-Javadoc)
790      * @see org.onap.aaf.auth.service.AuthzService#createOrUpdatePerm(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object, boolean, java.lang.String, java.lang.String, java.lang.String, java.util.List, java.util.List)
791      */
792     @ApiDoc( 
793             method = POST,  
794             path = "/authz/perm",
795             params = {},
796             expectedCode = 201,
797             errorCodes = {403,404,406,409}, 
798             text = { "Permission consists of:",
799                      "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
800                      + "is being protected</li>",
801                      "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
802                      + " instance of the type</li>",
803                      "<li>action - what kind of action is allowed</li></ul>",
804                      "Note: instance and action can be an *"
805                      }
806             )
807     @Override
808     public Result<Void> createPerm(final AuthzTrans trans,REQUEST rreq) {        
809         final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
810
811         final ServiceValidator v = new ServiceValidator();
812         if (v.perm(newPd).err()) {
813             return Result.err(Status.ERR_BadData,v.errs());
814         }
815
816         // User Permission mechanism
817         if(newPd.value.ns.indexOf('@')>0) {
818                 PermDAO.Data pdd = newPd.value;
819                 if(trans.user().equals(newPd.value.ns)) {
820                         CachedPermDAO permDAO = ques.permDAO();
821                         Result<List<PermDAO.Data>> rlpdd = permDAO.read(trans, pdd);
822                         if(rlpdd.notOK()) {
823                                 return Result.err(rlpdd);
824                         }
825                         if(!rlpdd.isEmpty()) {
826                                 return Result.err(Result.ERR_ConflictAlreadyExists,"Permission already exists"); 
827                         }
828
829                                 RoleDAO.Data rdd = new RoleDAO.Data();
830                                 rdd.ns = pdd.ns;
831                                 rdd.name = "user";
832
833                                 pdd.roles(true).add(rdd.fullName());
834                                 Result<PermDAO.Data> rpdd = permDAO.create(trans, pdd);
835                                 if(rpdd.notOK()) {
836                                         return Result.err(rpdd);
837                                 }
838                                 
839                         CachedRoleDAO roleDAO = ques.roleDAO();
840                         Result<List<RoleDAO.Data>> rlrdd = roleDAO.read(trans, rdd);
841                         if(rlrdd.notOK()) {
842                                 return Result.err(rlrdd);
843                         } else {
844                                 if(!rlrdd.isEmpty()) {
845                                         rdd = rlrdd.value.get(0);
846                                 }
847                         }
848                         
849                         String eperm = pdd.encode();
850                         rdd.perms(true).add(eperm);
851                         Result<Void> rv = roleDAO.update(trans, rdd);
852                         if(rv.notOK()) {
853                                 return rv;
854                         }
855                          
856                         CachedUserRoleDAO urDAO = ques.userRoleDAO();
857                         UserRoleDAO.Data urdd = new UserRoleDAO.Data();
858                         urdd.user = trans.user();
859                         urdd.ns = rdd.ns;
860                         urdd.rname = rdd.name;
861                         urdd.role = rdd.fullName();
862                         Result<List<UserRoleDAO.Data>> rlurdd = urDAO.read(trans, urdd);
863                         if(rlurdd.notOK()) {
864                                 return Result.err(rlrdd);
865                         } else if(rlurdd.isEmpty()) {
866                                 GregorianCalendar gc = trans.org().expiration(null, Expiration.UserInRole);
867                                 if(gc==null) {
868                                         return Result.err(Result.ERR_Policy,"Organzation does not grant Expiration for UserRole");
869                                 } else {
870                                         urdd.expires = gc.getTime();
871                                 }
872                                 Result<UserRoleDAO.Data> rurdd = urDAO.create(trans, urdd);
873                                 return Result.err(rurdd);
874                         }
875                         return rv;
876                 } else {
877                         return Result.err(Result.ERR_Security,"Only the User can create User Permissions");
878                 }
879         } else {
880                 // Does Perm Type exist as a Namespace?
881                 if(newPd.value.type.isEmpty() || ques.nsDAO().read(trans, newPd.value.fullType()).isOKhasData()) {
882                     return Result.err(Status.ERR_ConflictAlreadyExists,
883                             "Permission Type exists as a Namespace");
884                 }
885                 
886                 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, newPd.value,false,
887                     new Mapper.Memo() {
888                         @Override
889                         public String get() {
890                             return "Create Permission [" + 
891                                 newPd.value.fullType() + '|' + 
892                                 newPd.value.instance + '|' + 
893                                 newPd.value.action + ']';
894                         }
895                     },
896                     new MayChange() {
897                         private Result<NsDAO.Data> nsd;
898                         @Override
899                         public Result<?> mayChange() {
900                             if (nsd==null) {
901                                 nsd = ques.mayUser(trans, trans.user(), newPd.value, Access.write);
902                             }
903                             return nsd;
904                         }
905                     });
906                 
907                 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, newPd.value.ns);
908                 if (nsr.notOKorIsEmpty()) {
909                     return Result.err(nsr);
910                 }
911                 switch(fd.status) {
912                     case OK:
913                         Result<String> rfc = func.createFuture(trans,fd.value, 
914                                 newPd.value.fullType() + '|' + newPd.value.instance + '|' + newPd.value.action,
915                                 trans.user(),
916                                 nsr.value.get(0),
917                                 FUTURE_OP.C);
918                         if (rfc.isOK()) {
919                             return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
920                                     newPd.value.ns,
921                                     newPd.value.type,
922                                     newPd.value.instance,
923                                     newPd.value.action);
924                         } else {
925                             return Result.err(rfc);
926                         }
927                     case Status.ACC_Now:
928                         return func.createPerm(trans, newPd.value, true);
929                     default:
930                         return Result.err(fd);
931                 }
932         }
933     }
934
935     @ApiDoc( 
936             method = GET,  
937             path = "/authz/perms/:type",
938             params = {"type|string|true"},
939             expectedCode = 200,
940             errorCodes = { 404,406 }, 
941             text = { "List All Permissions that match the :type element of the key" }
942             )
943     @Override
944     public Result<PERMS> getPermsByType(AuthzTrans trans, final String permType) {
945         final Validator v = new ServiceValidator();
946         if (v.nullOrBlank("PermType", permType).err()) {
947             return Result.err(Status.ERR_BadData,v.errs());
948         }
949
950         Result<List<PermDAO.Data>> rlpd = ques.getPermsByType(trans, permType);
951         if (rlpd.notOK()) {
952             return Result.err(rlpd);
953         }
954
955 //        We don't have instance & action for mayUserView... do we want to loop through all returned here as well as in mapper?
956 //        Result<NsDAO.Data> r;
957 //        if ((r = ques.mayUserViewPerm(trans, trans.user(), permType)).notOK())return Result.err(r);
958         
959         PERMS perms = mapper.newInstance(API.PERMS);
960         if (!rlpd.isEmpty()) {
961             // Note: Mapper will restrict what can be viewed
962             return mapper.perms(trans, rlpd.value, perms, true);
963         }
964         return Result.ok(perms);
965     }
966     
967     @ApiDoc( 
968             method = GET,  
969             path = "/authz/perms/:type/:instance/:action",
970             params = {"type|string|true",
971                       "instance|string|true",
972                       "action|string|true"},
973             expectedCode = 200,
974             errorCodes = { 404,406 }, 
975             text = { "List Permissions that match key; :type, :instance and :action" }
976             )
977     @Override
978     public Result<PERMS> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
979         final Validator v = new ServiceValidator();
980         if (v.nullOrBlank("PermType", type).err()
981                 || v.nullOrBlank("PermInstance", instance).err()
982                 || v.nullOrBlank("PermAction", action).err()) {
983             return Result.err(Status.ERR_BadData,v.errs());
984         }
985         
986         Result<List<PermDAO.Data>> rlpd = ques.getPermsByName(trans, type, instance, action);
987         if (rlpd.notOK()) {
988             return Result.err(rlpd);
989         }
990
991         PERMS perms = mapper.newInstance(API.PERMS);
992         if (!rlpd.isEmpty()) {
993             // Note: Mapper will restrict what can be viewed
994             return mapper.perms(trans, rlpd.value, perms, true);
995         }
996         return Result.ok(perms);
997     }
998
999     @ApiDoc( 
1000             method = GET,  
1001             path = "/authz/perms/user/:user",
1002             params = {"user|string|true"},
1003             expectedCode = 200,
1004             errorCodes = { 404,406 }, 
1005             text = { "List All Permissions that match user :user",
1006                      "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>"}
1007             )
1008     @Override
1009     public Result<PERMS> getPermsByUser(AuthzTrans trans, String user) {
1010         final Validator v = new ServiceValidator();
1011         if (v.nullOrBlank("User", user).err()) {
1012             return Result.err(Status.ERR_BadData,v.errs());
1013         }
1014
1015         PermLookup pl = PermLookup.get(trans,ques,user);
1016         Result<List<PermDAO.Data>> rlpd = pl.getPerms(trans.requested(force));
1017         if (rlpd.notOK()) {
1018             return Result.err(rlpd);
1019         }
1020         
1021         PERMS perms = mapper.newInstance(API.PERMS);
1022         
1023         if (rlpd.isEmpty()) {
1024             return Result.ok(perms);
1025         }
1026         // Note: Mapper will restrict what can be viewed
1027         //   if user is the same as that which is looked up, no filtering is required
1028         return mapper.perms(trans, rlpd.value, 
1029                 perms, 
1030                 !user.equals(trans.user()));
1031     }
1032
1033     @ApiDoc( 
1034             method = GET,  
1035             path = "/authz/perms/user/:user/scope/:scope",
1036             params = {"user|string|true","scope|string|true"},
1037             expectedCode = 200,
1038             errorCodes = { 404,406 }, 
1039             text = { "List All Permissions that match user :user, filtered by NS (Scope)",
1040                      "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
1041                      "<p>'scope' must be expressed as NSs separated by ':'</p>"
1042                     }
1043             )
1044     @Override
1045     public Result<PERMS> getPermsByUserScope(AuthzTrans trans, String user, String[] scopes) {
1046         final Validator v = new ServiceValidator();
1047         if (v.nullOrBlank("User", user).err()) {
1048             return Result.err(Status.ERR_BadData,v.errs());
1049         }
1050
1051         Result<List<PermDAO.Data>> rlpd = ques.getPermsByUser(trans, user, trans.requested(force));
1052         if (rlpd.notOK()) {
1053             return Result.err(rlpd);
1054         }
1055         
1056         PERMS perms = mapper.newInstance(API.PERMS);
1057         
1058         if (rlpd.isEmpty()) {
1059             return Result.ok(perms);
1060         }
1061         // Note: Mapper will restrict what can be viewed
1062         //   if user is the same as that which is looked up, no filtering is required
1063         return mapper.perms(trans, rlpd.value, 
1064                 perms, 
1065                 scopes,
1066                 !user.equals(trans.user()));
1067     }
1068
1069     @ApiDoc( 
1070             method = POST,  
1071             path = "/authz/perms/user/:user",
1072             params = {"user|string|true"},
1073             expectedCode = 200,
1074             errorCodes = { 404,406 }, 
1075             text = { "List All Permissions that match user :user",
1076                      "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
1077                      "",
1078                      "Present Queries as one or more Permissions (see ContentType Links below for format).",
1079                      "",
1080                      "If the Caller is Granted this specific Permission, and the Permission is valid",
1081                      "  for the User, it will be included in response Permissions, along with",
1082                      "  all the normal permissions on the 'GET' version of this call.  If it is not",
1083                      "  valid, or Caller does not have permission to see, it will be removed from the list",
1084                      "",
1085                      "  *Note: This design allows you to make one call for all expected permissions",
1086                      " The permission to be included MUST be:",
1087                      "     <user namespace>.access|:<ns|role|perm>[:key]|<create|read|write>",
1088                      "   examples:",
1089                      "     com.att.myns.access|:ns|write",
1090                      "     com.att.myns.access|:role:myrole|create",
1091                      "     com.att.myns.access|:perm:mytype:myinstance:myaction|read",
1092                      ""
1093                      }
1094             )
1095     @Override
1096     public Result<PERMS> getPermsByUser(AuthzTrans trans, PERMS _perms, String user) {
1097             PERMS perms = _perms;
1098         final Validator v = new ServiceValidator();
1099         if (v.nullOrBlank("User", user).err()) {
1100             return Result.err(Status.ERR_BadData,v.errs());
1101         }
1102         
1103         //////////////
1104         PermLookup pl = PermLookup.get(trans,ques,user);
1105         Result<List<PermDAO.Data>> rlpd = pl.getPerms(trans.requested(force));
1106         if (rlpd.notOK()) {
1107             return Result.err(rlpd);
1108         }
1109         
1110         /*//TODO 
1111           1) See if allowed to query
1112           2) See if User is allowed
1113           */
1114         Result<List<PermDAO.Data>> in = mapper.perms(trans, perms);
1115         if (in.isOKhasData()) {
1116             List<PermDAO.Data> out = rlpd.value;
1117             boolean ok;
1118             for (PermDAO.Data pdd : in.value) {
1119                 ok = false;
1120                 if ("access".equals(pdd.type)) {
1121                     Access access = Access.valueOf(pdd.action);
1122                     String[] mdkey = Split.splitTrim(':',pdd.instance);
1123                     if (mdkey.length>1) {
1124                         String type = mdkey[1];
1125                         if ("role".equals(type)) {
1126                             if (mdkey.length>2) {
1127                                 RoleDAO.Data rdd = new RoleDAO.Data();
1128                                 rdd.ns=pdd.ns;
1129                                 rdd.name=mdkey[2];
1130                                 ok = ques.mayUser(trans, trans.user(), rdd, Access.read).isOK() && ques.mayUser(trans, user, rdd , access).isOK();
1131                             }
1132                         } else if ("perm".equals(type)) {
1133                             if (mdkey.length>4) { // also need instance/action
1134                                 PermDAO.Data p = new PermDAO.Data();
1135                                 p.ns=pdd.ns;
1136                                 p.type=mdkey[2];
1137                                 p.instance=mdkey[3];
1138                                 p.action=mdkey[4];
1139                                 ok = ques.mayUser(trans, trans.user(), p, Access.read).isOK() && ques.mayUser(trans, user, p , access).isOK();
1140                             }
1141                         } else if ("ns".equals(type)) {
1142                             NsDAO.Data ndd = new NsDAO.Data();
1143                             ndd.name=pdd.ns;
1144                             ok = ques.mayUser(trans, trans.user(), ndd, Access.read).isOK() && ques.mayUser(trans, user, ndd , access).isOK();
1145                         }
1146                     }
1147                 }
1148                 if (ok) {
1149                     out.add(pdd);
1150                 }
1151             }
1152         }        
1153         
1154         perms = mapper.newInstance(API.PERMS);
1155         if (rlpd.isEmpty()) {
1156             return Result.ok(perms);
1157         }
1158         // Note: Mapper will restrict what can be viewed
1159         //   if user is the same as that which is looked up, no filtering is required
1160         return mapper.perms(trans, rlpd.value, 
1161                 perms, 
1162                 !user.equals(trans.user()));
1163     }
1164     
1165     @ApiDoc( 
1166             method = GET,  
1167             path = "/authz/perms/role/:role",
1168             params = {"role|string|true"},
1169             expectedCode = 200,
1170             errorCodes = { 404,406 }, 
1171             text = { "List All Permissions that are granted to :role" }
1172             )
1173     @Override
1174     public Result<PERMS> getPermsByRole(AuthzTrans trans,String role) {
1175         final Validator v = new ServiceValidator();
1176         if (v.nullOrBlank("Role", role).err()) {
1177             return Result.err(Status.ERR_BadData,v.errs());
1178         }
1179
1180         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques,role);
1181         if (rrdd.notOK()) {
1182             return Result.err(rrdd);
1183         }
1184
1185         Result<NsDAO.Data> r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
1186         if (r.notOK()) {
1187             return Result.err(r);
1188         }
1189
1190         PERMS perms = mapper.newInstance(API.PERMS);
1191
1192         Result<List<PermDAO.Data>> rlpd = ques.getPermsByRole(trans, role, trans.requested(force));
1193         if (rlpd.isOKhasData()) {
1194             // Note: Mapper will restrict what can be viewed
1195             return mapper.perms(trans, rlpd.value, perms, true);
1196         }
1197         return Result.ok(perms);
1198     }
1199
1200     @ApiDoc( 
1201             method = GET,  
1202             path = "/authz/perms/ns/:ns",
1203             params = {"ns|string|true"},
1204             expectedCode = 200,
1205             errorCodes = { 404,406 }, 
1206             text = { "List All Permissions that are in Namespace :ns" }
1207             )
1208     @Override
1209     public Result<PERMS> getPermsByNS(AuthzTrans trans,String ns) {
1210         final Validator v = new ServiceValidator();
1211         if (v.nullOrBlank("NS", ns).err()) {
1212             return Result.err(Status.ERR_BadData,v.errs());
1213         }
1214
1215         Result<NsDAO.Data> rnd = ques.deriveNs(trans, ns);
1216         if (rnd.notOK()) {
1217             return Result.err(rnd);
1218         }
1219
1220         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
1221         if (rnd.notOK()) {
1222             return Result.err(rnd);     
1223         }
1224         
1225         Result<List<PermDAO.Data>> rlpd = ques.permDAO().readNS(trans, ns);
1226         if (rlpd.notOK()) {
1227             return Result.err(rlpd);
1228         }
1229
1230         PERMS perms = mapper.newInstance(API.PERMS);
1231         if (!rlpd.isEmpty()) {
1232             // Note: Mapper will restrict what can be viewed
1233             return mapper.perms(trans, rlpd.value,perms, true);
1234         }
1235         return Result.ok(perms);
1236     }
1237     
1238     @ApiDoc( 
1239             method = PUT,  
1240             path =     "/authz/perm/:type/:instance/:action",
1241             params = {"type|string|true",
1242                       "instance|string|true",
1243                         "action|string|true"},
1244             expectedCode = 200,
1245             errorCodes = { 404,406, 409 }, 
1246             text = { "Rename the Permission referenced by :type :instance :action, and "
1247                     + "rename (copy/delete) to the Permission described in PermRequest" }
1248             )
1249     @Override
1250     public Result<Void> renamePerm(final AuthzTrans trans,REQUEST rreq, String origType, String origInstance, String origAction) {
1251         final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
1252         final ServiceValidator v = new ServiceValidator();
1253         if (v.perm(newPd).err()) {
1254             return Result.err(Status.ERR_BadData,v.errs());
1255         }
1256
1257         if (ques.mayUser(trans, trans.user(), newPd.value,Access.write).notOK()) {
1258             return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1259                     newPd.value.ns,newPd.value.type,newPd.value.instance,newPd.value.action);
1260         }
1261         
1262         Result<NsSplit> nss = ques.deriveNsSplit(trans, origType);
1263         Result<List<PermDAO.Data>> origRlpd = ques.permDAO().read(trans, nss.value.ns, nss.value.name, origInstance, origAction); 
1264         
1265         if (origRlpd.notOKorIsEmpty()) {
1266             return Result.err(Status.ERR_PermissionNotFound, 
1267                     "Permission [%s|%s|%s] does not exist",
1268                     origType,origInstance,origAction);
1269         }
1270         
1271         PermDAO.Data origPd = origRlpd.value.get(0);
1272
1273         if (!origPd.ns.equals(newPd.value.ns)) {
1274             return Result.err(Status.ERR_Denied, "Cannot change namespace with rename command. " +
1275                     "<new type> must start with [" + origPd.ns + "]");
1276         }
1277         
1278         if ( origPd.type.equals(newPd.value.type) && 
1279                 origPd.action.equals(newPd.value.action) && 
1280                 origPd.instance.equals(newPd.value.instance) ) {
1281             return Result.err(Status.ERR_ConflictAlreadyExists, "New Permission must be different than original permission");
1282         }
1283         
1284         Set<String> origRoles = origPd.roles(false);
1285         if (!origRoles.isEmpty()) {
1286             Set<String> roles = newPd.value.roles(true);
1287             for (String role : origPd.roles) {
1288                 roles.add(role); 
1289             }
1290         }    
1291         
1292         newPd.value.description = origPd.description;
1293         
1294         Result<Void> rv = null;
1295         
1296         rv = func.createPerm(trans, newPd.value, false);
1297         if (rv.isOK()) {
1298             rv = func.deletePerm(trans, origPd, true, false);
1299         }
1300         return rv;
1301     }
1302     
1303     @ApiDoc( 
1304             method = PUT,  
1305             path = "/authz/perm",
1306             params = {},
1307             expectedCode = 200,
1308             errorCodes = { 404,406 }, 
1309             text = { "Add Description Data to Perm" }
1310             )
1311     @Override
1312     public Result<Void> updatePermDescription(AuthzTrans trans, REQUEST from) {
1313         final Result<PermDAO.Data> pd = mapper.perm(trans, from);
1314         final ServiceValidator v = new ServiceValidator();
1315         if (v.perm(pd).err()) {
1316             return Result.err(Status.ERR_BadData,v.errs());
1317         }
1318         if (v.nullOrBlank("description", pd.value.description).err()) {
1319             return Result.err(Status.ERR_BadData,v.errs());
1320         }
1321         final PermDAO.Data perm = pd.value;
1322         if (ques.permDAO().read(trans, perm.ns, perm.type, perm.instance,perm.action).notOKorIsEmpty()) {
1323             return Result.err(Status.ERR_NotFound, "Permission [%s.%s|%s|%s] does not exist",
1324                 perm.ns,perm.type,perm.instance,perm.action);
1325         }
1326
1327         if (ques.mayUser(trans, trans.user(), perm, Access.write).notOK()) {
1328             return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1329                     perm.ns,perm.type,perm.instance,perm.action);
1330         }
1331
1332         Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, pd.value.ns);
1333         if (nsr.notOKorIsEmpty()) {
1334             return Result.err(nsr);
1335         }
1336
1337         Result<Void> rdr = ques.permDAO().addDescription(trans, perm.ns, perm.type, perm.instance,
1338                 perm.action, perm.description);
1339         if (rdr.isOK()) {
1340             return Result.ok();
1341         } else {
1342             return Result.err(rdr);
1343         }
1344
1345     }
1346     
1347     @ApiDoc(
1348             method = PUT,
1349             path = "/authz/role/perm",
1350             params = {},
1351             expectedCode = 201,
1352             errorCodes = {403,404,406,409},
1353             text = { "Set a permission's roles to roles given" }
1354            )
1355
1356     @Override
1357     public Result<Void> resetPermRoles(final AuthzTrans trans, REQUEST rreq) {
1358         final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
1359         if (updt.notOKorIsEmpty()) {
1360             return Result.err(updt);
1361         }
1362
1363         final ServiceValidator v = new ServiceValidator();
1364         if (v.perm(updt).err()) {
1365             return Result.err(Status.ERR_BadData,v.errs());
1366         }
1367
1368         Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), updt.value, Access.write);
1369         if (nsd.notOK()) {
1370             return Result.err(nsd);
1371         }
1372
1373         // Read full set to get CURRENT values
1374         Result<List<PermDAO.Data>> rcurr = ques.permDAO().read(trans, 
1375                 updt.value.ns, 
1376                 updt.value.type, 
1377                 updt.value.instance, 
1378                 updt.value.action);
1379         
1380         if (rcurr.notOKorIsEmpty()) {
1381             return Result.err(Status.ERR_PermissionNotFound, 
1382                     "Permission [%s.%s|%s|%s] does not exist",
1383                      updt.value.ns,updt.value.type,updt.value.instance,updt.value.action);
1384         }
1385         
1386         // Create a set of Update Roles, which are in Internal Format
1387         Set<String> updtRoles = new HashSet<>();
1388         Result<NsSplit> nss;
1389         for (String role : updt.value.roles(false)) {
1390             nss = ques.deriveNsSplit(trans, role);
1391             if (nss.isOK()) {
1392                 updtRoles.add(nss.value.ns + '|' + nss.value.name);
1393             } else {
1394                 trans.error().log(nss.errorString());
1395             }
1396         }
1397
1398         Result<Void> rv = null;
1399         
1400         for (PermDAO.Data curr : rcurr.value) {
1401             Set<String> currRoles = curr.roles(false);
1402             // must add roles to this perm, and add this perm to each role 
1403             // in the update, but not in the current            
1404             for (String role : updtRoles) {
1405                 if (!currRoles.contains(role)) {
1406                     Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1407                     if (key.isOKhasData()) {
1408                         Result<List<RoleDAO.Data>> rrd = ques.roleDAO().read(trans, key.value);
1409                         if (rrd.isOKhasData()) {
1410                             for (RoleDAO.Data r : rrd.value) {
1411                                 rv = func.addPermToRole(trans, r, curr, false);
1412                                 if (rv.notOK() && rv.status!=Result.ERR_ConflictAlreadyExists) {
1413                                     return Result.err(rv);
1414                                 }
1415                             }
1416                         } else {
1417                             return Result.err(rrd);
1418                         }
1419                     }
1420                 }
1421             }
1422             // similarly, must delete roles from this perm, and delete this perm from each role
1423             // in the update, but not in the current
1424             for (String role : currRoles) {
1425                 if (!updtRoles.contains(role)) {
1426                     Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1427                     if (key.isOKhasData()) {
1428                         Result<List<RoleDAO.Data>> rdd = ques.roleDAO().read(trans, key.value);
1429                         if (rdd.isOKhasData()) {
1430                             for (RoleDAO.Data r : rdd.value) {
1431                                 rv = func.delPermFromRole(trans, r, curr, true);
1432                                 if (rv.notOK() && rv.status!=Status.ERR_PermissionNotFound) {
1433                                     return Result.err(rv);
1434                                 }
1435                             }
1436                         }
1437                     }
1438                 }
1439             }                
1440         } 
1441         return rv==null?Result.ok():rv;        
1442     }
1443     
1444     @ApiDoc( 
1445             method = DELETE,
1446             path = "/authz/perm",
1447             params = {},
1448             expectedCode = 200,
1449             errorCodes = { 404,406 }, 
1450             text = { "Delete the Permission referenced by PermKey.",
1451                     "You cannot normally delete a permission which is still granted to roles,",
1452                     "however the \"force\" property allows you to do just that. To do this: Add",
1453                     "'force=true' as a query parameter.",
1454                     "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>" }
1455             )
1456     @Override
1457     public Result<Void> deletePerm(final AuthzTrans trans, REQUEST from) {
1458         Result<PermDAO.Data> pd = mapper.perm(trans, from);
1459         if (pd.notOK()) {
1460             return Result.err(pd);
1461         }
1462         final ServiceValidator v = new ServiceValidator();
1463         if (v.nullOrBlank(pd.value).err()) {
1464             return Result.err(Status.ERR_BadData,v.errs());
1465         }
1466         final PermDAO.Data perm = pd.value;
1467         if (ques.permDAO().read(trans, perm).notOKorIsEmpty()) {
1468             return Result.err(Status.ERR_PermissionNotFound, "Permission [%s.%s|%s|%s] does not exist",
1469                     perm.ns,perm.type,perm.instance,perm.action    );
1470         }
1471         
1472         Result<FutureDAO.Data> fd = mapper.future(trans,PermDAO.TABLE,from,perm,false,
1473                 new Mapper.Memo() {
1474                     @Override
1475                     public String get() {
1476                         return "Delete Permission [" + perm.fullPerm() + ']';
1477                     }
1478                 },
1479             new MayChange() {
1480                 private Result<NsDAO.Data> nsd;
1481                 @Override
1482                 public Result<?> mayChange() {
1483                     if (nsd==null) {
1484                         nsd = ques.mayUser(trans, trans.user(), perm, Access.write);
1485                     }
1486                     return nsd;
1487                 }
1488             });
1489         
1490         switch(fd.status) {
1491         case OK:
1492             Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, perm.ns);
1493             if (nsr.notOKorIsEmpty()) {
1494                 return Result.err(nsr);
1495             }
1496             
1497             Result<String> rfc = func.createFuture(trans, fd.value, 
1498                     perm.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
1499             if (rfc.isOK()) {
1500                 return Result.err(Status.ACC_Future, "Perm Deletion [%s] is saved for future processing",perm.encode());
1501             } else { 
1502                 return Result.err(rfc);
1503             }
1504         case Status.ACC_Now:
1505             return func.deletePerm(trans,perm,trans.requested(force), false);
1506         default:
1507             return Result.err(fd);
1508         }            
1509     }    
1510     
1511     @ApiDoc( 
1512             method = DELETE,
1513             path = "/authz/perm/:name/:type/:action",
1514             params = {"type|string|true",
1515                       "instance|string|true",
1516                           "action|string|true"},
1517             expectedCode = 200,
1518             errorCodes = { 404,406 }, 
1519             text = { "Delete the Permission referenced by :type :instance :action",
1520                     "You cannot normally delete a permission which is still granted to roles,",
1521                     "however the \"force\" property allows you to do just that. To do this: Add",
1522                     "'force=true' as a query parameter",
1523                     "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>"}
1524             )
1525     @Override
1526     public Result<Void> deletePerm(AuthzTrans trans, String type, String instance, String action) {
1527         final Validator v = new ServiceValidator();
1528         if (v.nullOrBlank("Type",type)
1529             .nullOrBlank("Instance",instance)
1530             .nullOrBlank("Action",action)
1531             .err()) {
1532             return Result.err(Status.ERR_BadData,v.errs());
1533         }
1534         
1535         Result<PermDAO.Data> pd = ques.permFrom(trans, type, instance, action);
1536         if (pd.isOK()) {
1537             return func.deletePerm(trans, pd.value, trans.requested(force), false);
1538         } else {
1539             return Result.err(pd);
1540         }
1541     }
1542
1543 /***********************************
1544  * ROLE 
1545  ***********************************/
1546     @ApiDoc(
1547             method = POST,
1548             path = "/authz/role",
1549             params = {},
1550             expectedCode = 201,
1551             errorCodes = {403,404,406,409},
1552             text = {
1553
1554                 "Roles are part of Namespaces",
1555                 "Examples:",
1556                 "<ul><li>org.onap.aaf - The team that created and maintains AAF</li>",
1557                 "Roles do not include implied permissions for an App.  Instead, they contain explicit Granted Permissions by any Namespace in AAF (See Permissions)",
1558                 "Restrictions on Role Names:",
1559                 "<ul><li>Must start with valid Namespace name, terminated by . (dot/period)</li>",
1560                 "<li>Allowed Characters are a-zA-Z0-9._-</li>",
1561                 "<li>role names are Case Sensitive</li></ul>",
1562                 "The right questions to ask for defining and populating a Role in AAF, therefore, are:",
1563                 "<ul><li>'What Job Function does this represent?'</li>",
1564                 "<li>'Does this person perform this Job Function?'</li></ul>" }
1565            )
1566
1567     @Override
1568     public Result<Void> createRole(final AuthzTrans trans, REQUEST from) {
1569         final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1570         // Does Perm Type exist as a Namespace?
1571         if(rd.value.name.isEmpty() || ques.nsDAO().read(trans, rd.value.fullName()).isOKhasData()) {
1572             return Result.err(Status.ERR_ConflictAlreadyExists,
1573                     "Role exists as a Namespace");
1574         }
1575         final ServiceValidator v = new ServiceValidator();
1576         if (v.role(rd).err()) {
1577             return Result.err(Status.ERR_BadData,v.errs());
1578         }
1579         final RoleDAO.Data role = rd.value;
1580         if (ques.roleDAO().read(trans, role.ns, role.name).isOKhasData()) {
1581             return Result.err(Status.ERR_ConflictAlreadyExists, "Role [" + role.fullName() + "] already exists");
1582         }
1583
1584         Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
1585             new Mapper.Memo() {
1586                 @Override
1587                 public String get() {
1588                     return "Create Role [" + 
1589                         rd.value.fullName() + 
1590                         ']';
1591                 }
1592             },
1593             new MayChange() {
1594                 private Result<NsDAO.Data> nsd;
1595                 @Override
1596                 public Result<?> mayChange() {
1597                     if (nsd==null) {
1598                         nsd = ques.mayUser(trans, trans.user(), role, Access.write);
1599                     }
1600                     return nsd;
1601                 }
1602             });
1603         
1604         Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
1605         if (nsr.notOKorIsEmpty()) {
1606             return Result.err(nsr);
1607         }
1608
1609         switch(fd.status) {
1610             case OK:
1611                 Result<String> rfc = func.createFuture(trans, fd.value, 
1612                         role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.C);
1613                 if (rfc.isOK()) {
1614                     return Result.err(Status.ACC_Future, "Role [%s.%s] is saved for future processing",
1615                             rd.value.ns,
1616                             rd.value.name);
1617                 } else { 
1618                     return Result.err(rfc);
1619                 }
1620             case Status.ACC_Now:
1621                 Result<RoleDAO.Data> rdr = ques.roleDAO().create(trans, role);
1622                 if (rdr.isOK()) {
1623                     return Result.ok();
1624                 } else {
1625                     return Result.err(rdr);
1626                 }
1627             default:
1628                 return Result.err(fd);
1629         }
1630     }
1631
1632     /* (non-Javadoc)
1633      * @see org.onap.aaf.auth.service.AuthzService#getRolesByName(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1634      */
1635     @ApiDoc(
1636             method = GET,
1637             path = "/authz/roles/:role",
1638             params = {"role|string|true"}, 
1639             expectedCode = 200,
1640             errorCodes = {404,406},
1641             text = { "List Roles that match :role",
1642                      "Note: You must have permission to see any given role"
1643                    }
1644            )
1645     @Override
1646     public Result<ROLES> getRolesByName(AuthzTrans trans, String role) {
1647         final Validator v = new ServiceValidator();
1648         if (v.nullOrBlank("Role", role).err()) {
1649             return Result.err(Status.ERR_BadData,v.errs());
1650         }
1651         
1652         // Determine if User can ask this question
1653         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
1654         if (rrdd.isOKhasData()) {
1655             Result<NsDAO.Data> r;
1656             if ((r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read)).notOK()) {
1657                 return Result.err(r);
1658             }
1659         } else {
1660             return Result.err(rrdd);
1661         }
1662         
1663         // Look up data
1664         int query = role.indexOf('?');
1665         Result<List<RoleDAO.Data>> rlrd = ques.getRolesByName(trans, query<0?role:role.substring(0, query));
1666         if (rlrd.isOK()) {
1667             // Note: Mapper will restrict what can be viewed
1668             ROLES roles = mapper.newInstance(API.ROLES);
1669             return mapper.roles(trans, rlrd.value, roles, true);
1670         } else {
1671             return Result.err(rlrd);
1672         }
1673     }
1674
1675     /* (non-Javadoc)
1676      * @see org.onap.aaf.auth.service.AuthzService#getRolesByUser(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1677      */
1678     @ApiDoc(
1679             method = GET,
1680             path = "/authz/roles/user/:name",
1681             params = {"name|string|true"},
1682             expectedCode = 200,
1683             errorCodes = {404,406},
1684             text = { "List all Roles that match user :name",
1685                      "'user' must be expressed as full identity (ex: id@full.domain.com)",
1686                         "Note: You must have permission to see any given role"
1687             }
1688            )
1689
1690     @Override
1691     public Result<ROLES> getRolesByUser(AuthzTrans trans, String user) {
1692         final Validator v = new ServiceValidator();
1693         if (v.nullOrBlank("User", user).err()) {
1694             return Result.err(Status.ERR_BadData,v.errs());
1695         }
1696
1697         ROLES roles = mapper.newInstance(API.ROLES);
1698         // Get list of roles per user, then add to Roles as we go
1699         Result<List<RoleDAO.Data>> rlrd;
1700         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, user);
1701         if (rlurd.isOKhasData()) {
1702             for (UserRoleDAO.Data urd : rlurd.value ) {
1703                 rlrd = ques.roleDAO().read(trans, urd.ns,urd.rname);
1704                 // Note: Mapper will restrict what can be viewed
1705                 //   if user is the same as that which is looked up, no filtering is required
1706                 if (rlrd.isOKhasData()) {
1707                     mapper.roles(trans, rlrd.value,roles, !user.equals(trans.user()));
1708                 }
1709             }
1710         }
1711         return Result.ok(roles);
1712     }
1713
1714     /*
1715      * (non-Javadoc)
1716      * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1717      */
1718     @ApiDoc(
1719             method = GET,
1720             path = "/authz/roles/ns/:ns",
1721             params = {"ns|string|true"},
1722             expectedCode = 200,
1723             errorCodes = {404,406},
1724             text = { "List all Roles for the Namespace :ns", 
1725                          "Note: You must have permission to see any given role"
1726             }
1727            )
1728
1729     @Override
1730     public Result<ROLES> getRolesByNS(AuthzTrans trans, String ns) {
1731         final Validator v = new ServiceValidator();
1732         if (v.nullOrBlank("NS", ns).err()) {
1733             return Result.err(Status.ERR_BadData,v.errs());
1734         }
1735         
1736         // check if user is allowed to view NS
1737         Result<NsDAO.Data> rnsd = ques.deriveNs(trans, ns); 
1738         if (rnsd.notOK()) {
1739             return Result.err(rnsd);     
1740         }
1741         rnsd = ques.mayUser(trans, trans.user(), rnsd.value, Access.read);
1742         if (rnsd.notOK()) {
1743             return Result.err(rnsd);     
1744         }
1745
1746         TimeTaken tt = trans.start("MAP Roles by NS to Roles", Env.SUB);
1747         try {
1748             ROLES roles = mapper.newInstance(API.ROLES);
1749             // Get list of roles per user, then add to Roles as we go
1750             Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().readNS(trans, ns);
1751             if (rlrd.isOK()) {
1752                 if (!rlrd.isEmpty()) {
1753                     // Note: Mapper doesn't need to restrict what can be viewed, because we did it already.
1754                     mapper.roles(trans,rlrd.value,roles,false);
1755                 }
1756                 return Result.ok(roles);
1757             } else {
1758                 return Result.err(rlrd);
1759             }
1760         } finally {
1761             tt.done();
1762         }
1763     }
1764
1765     /*
1766      * (non-Javadoc)
1767      * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1768      */
1769     @ApiDoc(
1770             method = GET,
1771             path = "/authz/roles/name/:name",
1772             params = {"name|string|true"},
1773             expectedCode = 200,
1774             errorCodes = {404,406},
1775             text = { "List all Roles for only the Name of Role (without Namespace)", 
1776                          "Note: You must have permission to see any given role"
1777             }
1778            )
1779     @Override
1780     public Result<ROLES> getRolesByNameOnly(AuthzTrans trans, String name) {
1781         final Validator v = new ServiceValidator();
1782         if (v.nullOrBlank("Name", name).err()) {
1783             return Result.err(Status.ERR_BadData,v.errs());
1784         }
1785         
1786         // User Mapper to make sure user is allowed to view NS
1787
1788         TimeTaken tt = trans.start("MAP Roles by Name to Roles", Env.SUB);
1789         try {
1790             ROLES roles = mapper.newInstance(API.ROLES);
1791             // Get list of roles per user, then add to Roles as we go
1792             Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().readName(trans, name);
1793             if (rlrd.isOK()) {
1794                 if (!rlrd.isEmpty()) {
1795                     // Note: Mapper will restrict what can be viewed
1796                     mapper.roles(trans,rlrd.value,roles,true);
1797                 }
1798                 return Result.ok(roles);
1799             } else {
1800                 return Result.err(rlrd);
1801             }
1802         } finally {
1803             tt.done();
1804         }
1805     }
1806
1807     @ApiDoc(
1808             method = GET,
1809             path = "/authz/roles/perm/:type/:instance/:action",
1810             params = {"type|string|true",
1811                       "instance|string|true",
1812                       "action|string|true"},
1813             expectedCode = 200,
1814             errorCodes = {404,406},
1815             text = { "Find all Roles containing the given Permission." +
1816                      "Permission consists of:",
1817                      "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1818                      + "is being protected</li>",
1819                      "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1820                      + " instance of the type</li>",
1821                      "<li>action - what kind of action is allowed</li></ul>",
1822                      "Notes: instance and action can be an *",
1823                      "       You must have permission to see any given role"
1824                      }
1825            )
1826
1827     @Override
1828     public Result<ROLES> getRolesByPerm(AuthzTrans trans, String type, String instance, String action) {
1829         final Validator v = new ServiceValidator();
1830         if (v.permType(type)
1831             .permInstance(instance)
1832             .permAction(action)
1833             .err()) {
1834             return Result.err(Status.ERR_BadData,v.errs());
1835         }
1836         
1837         TimeTaken tt = trans.start("Map Perm Roles Roles", Env.SUB);
1838         try {
1839             ROLES roles = mapper.newInstance(API.ROLES);
1840             // Get list of roles per user, then add to Roles as we go
1841             Result<NsSplit> nsSplit = ques.deriveNsSplit(trans, type);
1842             if (nsSplit.isOK()) {
1843                 PermDAO.Data pdd = new PermDAO.Data(nsSplit.value, instance, action);
1844                 Result<?> res;
1845                 if ((res=ques.mayUser(trans, trans.user(), pdd, Question.Access.read)).notOK()) {
1846                     return Result.err(res);
1847                 }
1848                 
1849                 Result<List<PermDAO.Data>> pdlr = ques.permDAO().read(trans, pdd);
1850                 if (pdlr.isOK())for (PermDAO.Data pd : pdlr.value) {
1851                     Result<List<RoleDAO.Data>> rlrd;
1852                     for (String r : pd.roles) {
1853                         Result<String[]> rs = RoleDAO.Data.decodeToArray(trans, ques, r);
1854                         if (rs.isOK()) {
1855                             rlrd = ques.roleDAO().read(trans, rs.value[0],rs.value[1]);
1856                             // Note: Mapper will restrict what can be viewed
1857                             if (rlrd.isOKhasData()) {
1858                                 mapper.roles(trans,rlrd.value,roles,true);
1859                             }
1860                         }
1861                     }
1862                 }
1863             }
1864             return Result.ok(roles);
1865         } finally {
1866             tt.done();
1867         }
1868     }
1869
1870     @ApiDoc(
1871             method = PUT,
1872             path = "/authz/role",
1873             params = {},
1874             expectedCode = 200,
1875             errorCodes = {404,406},
1876             text = { "Add Description Data to a Role" }
1877            )
1878
1879     @Override
1880     public Result<Void> updateRoleDescription(AuthzTrans trans, REQUEST from) {
1881         final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1882         final ServiceValidator v = new ServiceValidator();
1883         if (v.role(rd).err()) {
1884             return Result.err(Status.ERR_BadData,v.errs());
1885         } {
1886         if (v.nullOrBlank("description", rd.value.description).err()) {
1887             return Result.err(Status.ERR_BadData,v.errs());
1888         }
1889         }
1890         final RoleDAO.Data role = rd.value;
1891         if (ques.roleDAO().read(trans, role.ns, role.name).notOKorIsEmpty()) {
1892             return Result.err(Status.ERR_NotFound, "Role [" + role.fullName() + "] does not exist");
1893         }
1894
1895         if (ques.mayUser(trans, trans.user(), role, Access.write).notOK()) {
1896             return Result.err(Status.ERR_Denied, "You do not have approval to change " + role.fullName());
1897         }
1898
1899         Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
1900         if (nsr.notOKorIsEmpty()) {
1901             return Result.err(nsr);
1902         }
1903
1904         Result<Void> rdr = ques.roleDAO().addDescription(trans, role.ns, role.name, role.description);
1905         if (rdr.isOK()) {
1906             return Result.ok();
1907         } else {
1908             return Result.err(rdr);
1909         }
1910
1911     }
1912     
1913     @ApiDoc(
1914             method = POST,
1915             path = "/authz/role/perm",
1916             params = {},
1917             expectedCode = 201,
1918             errorCodes = {403,404,406,409},
1919             text = { "Grant a Permission to a Role",
1920                      "Permission consists of:", 
1921                      "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1922                      + "is being protected</li>",
1923                      "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1924                      + " instance of the type</li>",
1925                      "<li>action - what kind of action is allowed</li></ul>",
1926                      "Note: instance and action can be an *",
1927                      "Note: Using the \"force\" property will create the Permission, if it doesn't exist AND the requesting " +
1928                      " ID is allowed to create.  It will then grant",
1929                      "  the permission to the role in one step. To do this: add 'force=true' as a query parameter."
1930                     }
1931            )
1932
1933     @Override
1934     public Result<Void> addPermToRole(final AuthzTrans trans, REQUEST rreq) {
1935         // Translate Request into Perm and Role Objects
1936         final Result<PermDAO.Data> rpd = mapper.permFromRPRequest(trans, rreq);
1937         if (rpd.notOKorIsEmpty()) {
1938             return Result.err(rpd);
1939         }
1940         final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
1941         if (rrd.notOKorIsEmpty()) {
1942             return Result.err(rrd);
1943         }
1944         
1945         // Validate Role and Perm values
1946         final ServiceValidator v = new ServiceValidator();
1947         if (v.perm(rpd.value)
1948             .role(rrd.value)
1949             .err()) {
1950             return Result.err(Status.ERR_BadData,v.errs());
1951         }
1952
1953         Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().read(trans, rrd.value.ns, rrd.value.name);
1954         if (rlrd.notOKorIsEmpty()) {
1955             return Result.err(Status.ERR_RoleNotFound, "Role [%s] does not exist", rrd.value.fullName());
1956         }
1957         
1958         // Check Status of Data in DB (does it exist)
1959         Result<List<PermDAO.Data>> rlpd = ques.permDAO().read(trans, rpd.value.ns, 
1960                 rpd.value.type, rpd.value.instance, rpd.value.action);
1961         PermDAO.Data createPerm = null; // if not null, create first
1962         if (rlpd.notOKorIsEmpty()) { // Permission doesn't exist
1963             if (trans.requested(force)) {
1964                 // Remove roles from perm data object so we just create the perm here
1965                 createPerm = rpd.value;
1966                 createPerm.roles.clear();
1967             } else {
1968                 return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist", 
1969                         rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action);
1970             }
1971         } else {
1972             if (rlpd.value.get(0).roles(false).contains(rrd.value.encode())) {
1973                 return Result.err(Status.ERR_ConflictAlreadyExists,
1974                         "Permission [%s.%s|%s|%s] already granted to Role [%s.%s]",
1975                         rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action,
1976                         rrd.value.ns,rrd.value.name
1977                     );
1978             }
1979         }
1980
1981         
1982         Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, rpd.value,true, // Allow grants to create Approvals
1983                 new Mapper.Memo() {
1984                     @Override
1985                     public String get() {
1986                         return "Grant Permission [" + rpd.value.fullPerm() + ']' +
1987                             " to Role [" + rrd.value.fullName() + "]";
1988                     }
1989                 },
1990                 new MayChange() {
1991                     private Result<NsDAO.Data> nsd;
1992                     @Override
1993                     public Result<?> mayChange() {
1994                         if (nsd==null) {
1995                             nsd = ques.mayUser(trans, trans.user(), rpd.value, Access.write);
1996                             if(nsd.notOK()) {
1997                                 trans.requested(REQD_TYPE.future,true);
1998                             }
1999                         }
2000                         return nsd;
2001                     }
2002                 });
2003         Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rpd.value.ns);
2004         if (nsr.notOKorIsEmpty()) {
2005             return Result.err(nsr);
2006         }
2007         switch(fd.status) {
2008                 case OK:
2009                     Result<String> rfc = func.createFuture(trans,fd.value, 
2010                             rpd.value.fullPerm(),
2011                             trans.user(),
2012                             nsr.value.get(0),
2013                             FUTURE_OP.G);
2014                     if (rfc.isOK()) {
2015                         return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
2016                                 rpd.value.ns,
2017                                 rpd.value.type,
2018                                 rpd.value.instance,
2019                                 rpd.value.action);
2020                     } else { 
2021                         return Result.err(rfc);
2022                     }
2023                 case Status.ACC_Now:
2024                     Result<Void> rv = null;
2025                     if (createPerm!=null) {// has been validated for creating
2026                         rv = func.createPerm(trans, createPerm, false);
2027                     }
2028                     if (rv==null || rv.isOK()) {
2029                         rv = func.addPermToRole(trans, rrd.value, rpd.value, false);
2030                     }
2031                     return rv;
2032                 default:
2033                     return Result.err(fd);
2034         }
2035         
2036     }
2037
2038     /**
2039      * Delete Perms from Roles (UnGrant)
2040      * @param trans
2041      * @param roleFullName
2042      * @return
2043      */
2044     @ApiDoc(
2045             method = DELETE,
2046             path = "/authz/role/:role/perm",
2047             params = {"role|string|true"},
2048             expectedCode = 200,
2049             errorCodes = {404,406},
2050             text = { "Ungrant a permission from Role :role" }
2051            )
2052
2053     @Override
2054     public Result<Void> delPermFromRole(final AuthzTrans trans, REQUEST rreq) {
2055         final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
2056         if (updt.notOKorIsEmpty()) {
2057             return Result.err(updt);
2058         }
2059         final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
2060         if (rrd.notOKorIsEmpty()) {
2061             return Result.err(rrd);
2062         }
2063
2064         final ServiceValidator v = new ServiceValidator();
2065         if (v.nullOrBlank(updt.value)
2066             .nullOrBlank(rrd.value)
2067             .err()) {
2068             return Result.err(Status.ERR_BadData,v.errs());
2069         }
2070
2071         return delPermFromRole(trans, updt.value,rrd.value, rreq);
2072     }
2073         
2074     private Result<Void> delPermFromRole(final AuthzTrans trans, PermDAO.Data pdd, RoleDAO.Data rdd, REQUEST rreq) {        
2075         Result<List<PermDAO.Data>> rlpd = ques.permDAO().read(trans, pdd.ns, pdd.type, 
2076                 pdd.instance, pdd.action);
2077         
2078         if (rlpd.notOKorIsEmpty()) {
2079             return Result.err(Status.ERR_PermissionNotFound, 
2080                 "Permission [%s.%s|%s|%s] does not exist",
2081                     pdd.ns,pdd.type,pdd.instance,pdd.action);
2082         }
2083         
2084         Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, pdd,true, // allow ungrants requests
2085                 new Mapper.Memo() {
2086                     @Override
2087                     public String get() {
2088                         return "Ungrant Permission [" + pdd.fullPerm() + ']' +
2089                             " from Role [" + rdd.fullName() + "]";
2090                     }
2091                 },
2092                 new MayChange() {
2093                     private Result<NsDAO.Data> nsd;
2094                     @Override
2095                     public Result<?> mayChange() {
2096                         if (nsd==null) {
2097                             nsd = ques.mayUser(trans, trans.user(), pdd, Access.write);
2098                         }
2099                         return nsd;
2100                     }
2101                 });
2102         Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, pdd.ns);
2103         if (nsr.notOKorIsEmpty()) {
2104             return Result.err(nsr);
2105         }
2106         switch(fd.status) {
2107             case OK:
2108                 Result<String> rfc = func.createFuture(trans,fd.value, 
2109                         pdd.fullPerm(),
2110                         trans.user(),
2111                         nsr.value.get(0),
2112                         FUTURE_OP.UG
2113                         );
2114                 if (rfc.isOK()) {
2115                     return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
2116                             pdd.ns,
2117                             pdd.type,
2118                             pdd.instance,
2119                             pdd.action);
2120                 } else {
2121                     return Result.err(rfc);
2122                 }
2123             case Status.ACC_Now:
2124                 return func.delPermFromRole(trans, rdd, pdd, false);
2125             default:
2126                 return Result.err(fd);
2127         }
2128     }
2129     
2130 /*
2131     @ApiDoc(
2132             method = DELETE,
2133             path = "/authz/role/:role/perm/:type/:instance/:action",
2134             params = {"role|string|true",
2135                          "perm type|string|true",
2136                          "perm instance|string|true",
2137                          "perm action|string|true"
2138                 },
2139             expectedCode = 200,
2140             errorCodes = {404,406},
2141             text = { "Ungrant a single permission from Role :role with direct key" }
2142            )
2143 */
2144     @Override
2145     public Result<Void> delPermFromRole(AuthzTrans trans, String role, String type, String instance, String action) {
2146         Result<Data> rpns = ques.deriveNs(trans, type);
2147         if (rpns.notOKorIsEmpty()) {
2148             return Result.err(rpns);
2149         }
2150         
2151             final Validator v = new ServiceValidator();
2152         if (v.role(role)
2153             .permType(rpns.value.name,rpns.value.parent)
2154             .permInstance(instance)
2155             .permAction(action)
2156             .err()) {
2157             return Result.err(Status.ERR_BadData,v.errs());
2158         }
2159         
2160             Result<Data> rrns = ques.deriveNs(trans, role);
2161             if (rrns.notOKorIsEmpty()) {
2162                 return Result.err(rrns);
2163             }
2164             
2165         final Result<List<RoleDAO.Data>> rrd = ques.roleDAO().read(trans, rrns.value.parent, rrns.value.name);
2166         if (rrd.notOKorIsEmpty()) {
2167             return Result.err(rrd);
2168         }
2169         
2170         final Result<List<PermDAO.Data>> rpd = ques.permDAO().read(trans, rpns.value.parent, rpns.value.name, instance, action);
2171         if (rpd.notOKorIsEmpty()) {
2172             return Result.err(rpd);
2173         }
2174
2175         
2176         return delPermFromRole(trans,rpd.value.get(0), rrd.value.get(0), mapper.ungrantRequest(trans, role, type, instance, action));
2177     }
2178     
2179     @ApiDoc(
2180             method = DELETE,
2181             path = "/authz/role/:role",
2182             params = {"role|string|true"},
2183             expectedCode = 200,
2184             errorCodes = {404,406},
2185             text = { "Delete the Role named :role"}
2186            )
2187
2188     @Override
2189     public Result<Void> deleteRole(AuthzTrans trans, String role)  {
2190         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
2191         if (rrdd.isOKhasData()) {
2192             final ServiceValidator v = new ServiceValidator();
2193             if (v.nullOrBlank(rrdd.value).err()) { 
2194                 return Result.err(Status.ERR_BadData,v.errs());
2195             }
2196             return func.deleteRole(trans, rrdd.value, false, false);
2197         } else {
2198             return Result.err(rrdd);
2199         }
2200     }
2201
2202     @ApiDoc(
2203             method = DELETE,
2204             path = "/authz/role",
2205             params = {},
2206             expectedCode = 200,
2207             errorCodes = { 404,406 },
2208             text = { "Delete the Role referenced by RoleKey",
2209                     "You cannot normally delete a role which still has permissions granted or users assigned to it,",
2210                     "however the \"force\" property allows you to do just that. To do this: Add 'force=true'",
2211                     "as a query parameter.",
2212                     "<p>WARNING: Using force will remove all users and permission from this role. Use with care.</p>"}
2213            )
2214
2215     @Override
2216     public Result<Void> deleteRole(final AuthzTrans trans, REQUEST from) {
2217         final Result<RoleDAO.Data> rd = mapper.role(trans, from);
2218         final ServiceValidator v = new ServiceValidator();
2219         if (rd==null) {
2220             return Result.err(Status.ERR_BadData,"Request does not contain Role");
2221         }
2222         if (v.nullOrBlank(rd.value).err()) {
2223             return Result.err(Status.ERR_BadData,v.errs());
2224         }
2225         final RoleDAO.Data role = rd.value;
2226         if (ques.roleDAO().read(trans, role).notOKorIsEmpty() && !trans.requested(force)) {
2227             return Result.err(Status.ERR_RoleNotFound, "Role [" + role.fullName() + "] does not exist");
2228         }
2229
2230         Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
2231             () -> "Delete Role [" + role.fullName() + ']'
2232                     + " and all attached user roles",
2233             new MayChange() {
2234                 private Result<NsDAO.Data> nsd;
2235                 @Override
2236                 public Result<?> mayChange() {
2237                     if (nsd==null) {
2238                         nsd = ques.mayUser(trans, trans.user(), role, Access.write);
2239                     }
2240                     return nsd;
2241                 }
2242             });
2243         
2244         switch(fd.status) {
2245         case OK:
2246             Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
2247             if (nsr.notOKorIsEmpty()) {
2248                 return Result.err(nsr);
2249             }
2250             
2251             Result<String> rfc = func.createFuture(trans, fd.value, 
2252                     role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
2253             if (rfc.isOK()) {
2254                 return Result.err(Status.ACC_Future, "Role Deletion [%s.%s] is saved for future processing",
2255                         rd.value.ns,
2256                         rd.value.name);
2257             } else { 
2258                 return Result.err(rfc);
2259             }
2260         case Status.ACC_Now:
2261             return func.deleteRole(trans,role,trans.requested(force), true /*preapproved*/);
2262         default:
2263             return Result.err(fd);
2264     }
2265
2266     }
2267
2268 /***********************************
2269  * CRED 
2270  ***********************************/
2271     private class MayCreateCred implements MayChange {
2272         private Result<NsDAO.Data> nsd;
2273         private AuthzTrans trans;
2274         private CredDAO.Data cred;
2275         private Executor exec;
2276         
2277         public MayCreateCred(AuthzTrans trans, CredDAO.Data cred, Executor exec) {
2278             this.trans = trans;
2279             this.cred = cred;
2280             this.exec = exec;
2281         }
2282
2283         @Override
2284         public Result<?> mayChange() {
2285             if (nsd==null) {
2286                 nsd = ques.validNSOfDomain(trans, cred.id);
2287             }
2288             // is Ns of CredID valid?
2289             if (nsd.isOK()) {
2290                 try {
2291                     // Check Org Policy
2292                     if (trans.org().validate(trans,Policy.CREATE_MECHID, exec, cred.id)==null) {
2293                         return Result.ok(); 
2294                     } else {
2295                        Result<?> rmc = ques.mayUser(trans, trans.user(), nsd.value, Access.write);
2296                        if (rmc.isOKhasData()) {
2297                            return rmc;
2298                        }
2299                     }
2300                 } catch (Exception e) {
2301                     trans.warn().log(e);
2302                 }
2303             } else {
2304                 trans.warn().log(nsd.errorString());
2305             }
2306             return Result.err(Status.ERR_Denied,"%s is not allowed to create %s in %s",trans.user(),cred.id,cred.ns);
2307         }
2308     }
2309
2310     private class MayChangeCred implements MayChange {
2311         private static final String EXTEND = "extend";
2312                 private static final String RESET = "reset";
2313                 private static final String DELETE = "delete";
2314                 private Result<NsDAO.Data> nsd;
2315         private AuthzTrans trans;
2316         private CredDAO.Data cred;
2317                 private String action;
2318         public MayChangeCred(AuthzTrans trans, CredDAO.Data cred, String action) {
2319             this.trans = trans;
2320             this.cred = cred;
2321             this.action = action;
2322         }
2323
2324         @Override
2325         public Result<?> mayChange() {
2326             // User can change himself (but not create)
2327             if (nsd==null) {
2328                 nsd = ques.validNSOfDomain(trans, cred.id);
2329             }
2330             // Get the Namespace
2331             if (nsd.isOK()) {
2332                         String ns = nsd.value.name;
2333                         String user = trans.user();
2334                 String company;
2335                 String temp[] = Split.split('.',ns);
2336                 switch(temp.length) {
2337                         case 0:
2338                                 company = Defaults.AAF_NS;
2339                                 break;
2340                         case 1:
2341                                 company = temp[0];
2342                                 break;
2343                         default:
2344                                 company = temp[0] + '.' + temp[1];
2345                 }
2346                 switch(action) {
2347                         case DELETE:
2348                                 if(ques.isOwner(trans, user,ns) ||
2349                                    ques.isAdmin(trans, user,ns) ||
2350                                                    ques.isGranted(trans, user, ROOT_NS,"password",company,DELETE)) {
2351                                                 return Result.ok();
2352                                 }
2353                                 break;
2354                         case RESET:
2355                         case EXTEND:
2356                         if (ques.isGranted(trans, trans.user(), ROOT_NS,"password",company,action)) {
2357                             return Result.ok();
2358                         }
2359                         break;
2360                 }
2361             }
2362             return Result.err(Status.ERR_Denied,"%s is not allowed to %s %s in %s",trans.user(),action,cred.id,cred.ns);
2363         }
2364     }
2365
2366     private final long DAY_IN_MILLIS = 24*3600*1000L;
2367     
2368     @ApiDoc( 
2369             method = POST,  
2370             path = "/authn/cred",
2371             params = {},
2372             expectedCode = 201,
2373             errorCodes = {403,404,406,409}, 
2374             text = { "A credential consists of:",
2375                      "<ul><li>id - the ID to create within AAF. The domain is in reverse",
2376                      "order of Namespace (i.e. Users of Namespace com.att.myapp would be",
2377                      "AB1234@myapp.att.com</li>",
2378                      "<li>password - Company Policy Compliant Password</li></ul>",
2379                      "Note: AAF does support multiple credentials with the same ID.",
2380                      "Check with your organization if you have this implemented."
2381                      }
2382             )
2383     @Override
2384     public Result<Void> createUserCred(final AuthzTrans trans, REQUEST from) {
2385         final String cmdDescription = ("Create User Credential");
2386         TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2387         
2388         try {
2389             Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2390             if (rcred.isOKhasData()) {
2391                 rcred = ques.userCredSetup(trans, rcred.value);
2392                 
2393                 final ServiceValidator v = new ServiceValidator();
2394                 
2395                 if (v.cred(trans, trans.org(),rcred,true).err()) { // Note: Creates have stricter Validations 
2396                     return Result.err(Status.ERR_BadData,v.errs());
2397                 }
2398                 
2399
2400                 // 2016-4 Jonathan, New Behavior - If MechID is not registered with Org, deny creation
2401                 Identity mechID =  null;
2402                 Organization org = trans.org();
2403                 try {
2404                     mechID = org.getIdentity(trans, rcred.value.id);
2405                 } catch (Exception e1) {
2406                     trans.error().log(e1,rcred.value.id,"cannot be validated at this time");
2407                 }
2408                 if (mechID==null || !mechID.isFound()) { 
2409                     return Result.err(Status.ERR_Policy,"MechIDs must be registered with %s before provisioning in AAF",org.getName());
2410                 }
2411
2412                 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rcred.value.ns);
2413                 if (nsr.notOKorIsEmpty()) {
2414                     return Result.err(Status.ERR_NsNotFound,"Cannot provision %s on non-existent Namespace %s",mechID.id(),rcred.value.ns);
2415                 }
2416                 
2417
2418                 boolean firstID = false;
2419                 MayChange mc;
2420                 
2421                 CassExecutor exec = new CassExecutor(trans, func);
2422                 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, rcred.value.id);
2423                 if (rlcd.isOKhasData()) {
2424                     if (!org.canHaveMultipleCreds(rcred.value.id)) {
2425                         return Result.err(Status.ERR_ConflictAlreadyExists, "Credential exists");
2426                     }
2427                     Result<Boolean> rb;
2428                     for (CredDAO.Data curr : rlcd.value) {
2429                         // May not use the same password in the list
2430                         // Note: ASPR specifies character differences, but we don't actually store the
2431                         // password to validate char differences.
2432                         
2433 //                      byte[] rawCred = rcred.value.type==CredDAO.RAW?null:;                            return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2434                         if(rcred.value.type==CredDAO.FQI ) {
2435                                 if(curr.type==CredDAO.FQI) {
2436                                 return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2437                                 }
2438                         } else {
2439         
2440                                 rb = ques.userCredCheck(trans, curr, rcred.value.cred!=null?rcred.value.cred.array():null);
2441                                 if (rb.notOK()) {
2442                                     return Result.err(rb);
2443                                 } else if (rb.value){
2444                                     return Result.err(Status.ERR_Policy, "Credential content cannot be reused.");
2445                                 } else if ((Chrono.dateOnlyStamp(curr.expires).equals(Chrono.dateOnlyStamp(rcred.value.expires)) && curr.type==rcred.value.type)) {
2446                                     return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2447                                 }
2448                         }
2449                     }    
2450                 } else {
2451                     try {
2452                     // 2016-04-12 Jonathan If Caller is the Sponsor and is also an Owner of NS, allow without special Perm
2453                         String theMechID = rcred.value.id;
2454                         Boolean otherMechIDs = false;
2455                         // find out if this is the only mechID.  other MechIDs mean special handling (not automated)
2456                         for (CredDAO.Data cd : ques.credDAO().readNS(trans,nsr.value.get(0).name).value) {
2457                             if (!cd.id.equals(theMechID)) {
2458                                 otherMechIDs = true;
2459                                 break;
2460                             }
2461                         }
2462                         String reason;
2463                         // We can say "ID does not exist" here
2464                         if ((reason=org.validate(trans, Policy.CREATE_MECHID, exec, theMechID,trans.user(),otherMechIDs.toString()))!=null) {
2465                             return Result.err(Status.ERR_Denied, reason); 
2466                         }
2467                         firstID=true;
2468                     } catch (Exception e) {
2469                         return Result.err(e);
2470                     }
2471                 }
2472     
2473                 mc = new MayCreateCred(trans, rcred.value, exec);
2474                 
2475                 final CredDAO.Data cdd = rcred.value;
2476                 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false, // may want to enable in future.
2477                     new Mapper.Memo() {
2478                         @Override
2479                         public String get() {
2480                             return cmdDescription + " [" + 
2481                                 cdd.id + '|' 
2482                                 + cdd.type + '|' 
2483                                 + cdd.expires + ']';
2484                         }
2485                     },
2486                     mc);
2487                 
2488                 switch(fd.status) {
2489                     case OK:
2490                         Result<String> rfc = func.createFuture(trans, fd.value, 
2491                                 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2492                                 trans.user(), nsr.value.get(0), FUTURE_OP.C);
2493                         if (rfc.isOK()) {
2494                             return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s] is saved for future processing",
2495                                     rcred.value.id,
2496                                     Integer.toString(rcred.value.type),
2497                                     rcred.value.expires.toString());
2498                         } else { 
2499                             return Result.err(rfc);
2500                         }
2501                     case Status.ACC_Now:
2502                         try {
2503                             if (firstID) {
2504     //                            && !nsr.value.get(0).isAdmin(trans.getUserPrincipal().getName())) {
2505                                 Result<List<String>> admins = func.getAdmins(trans, nsr.value.get(0).name, false);
2506                                 // OK, it's a first ID, and not by NS Admin, so let's set TempPassword length
2507                                 // Note, we only do this on First time, because of possibility of 
2508                                 // prematurely expiring a production id
2509                                 if (admins.isOKhasData() && !admins.value.contains(trans.user())) {
2510                                     rcred.value.expires = org.expiration(null, Expiration.TempPassword).getTime();
2511                                 }
2512                             }
2513                         } catch (Exception e) {
2514                             trans.error().log(e, "While setting expiration to TempPassword");
2515                         }
2516                         
2517                         Result<?>udr = ques.credDAO().create(trans, rcred.value);
2518                         if (udr.isOK()) {
2519                             return Result.ok();
2520                         }
2521                         return Result.err(udr);
2522                     default:
2523                         return Result.err(fd);
2524                 }
2525
2526             } else {
2527                 return Result.err(rcred);
2528             }
2529         } finally {
2530             tt.done();
2531         }
2532     }
2533
2534     @ApiDoc(   
2535             method = GET,  
2536             path = "/authn/creds/ns/:ns",
2537             params = {"ns|string|true"},
2538             expectedCode = 200,
2539             errorCodes = {403,404,406}, 
2540             text = { "Return all IDs in Namespace :ns"
2541                      }
2542             )
2543     @Override
2544     public Result<USERS> getCredsByNS(AuthzTrans trans, String ns) {
2545         final Validator v = new ServiceValidator();
2546         if (v.ns(ns).err()) {
2547             return Result.err(Status.ERR_BadData,v.errs());
2548         }
2549         
2550         // check if user is allowed to view NS
2551         Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2552         if (rnd.notOK()) {
2553             return Result.err(rnd); 
2554         }
2555         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2556         if (rnd.notOK()) {
2557             return Result.err(rnd); 
2558         }
2559     
2560         TimeTaken tt = trans.start("MAP Creds by NS to Creds", Env.SUB);
2561         try {            
2562             USERS users = mapper.newInstance(API.USERS);
2563             Result<List<CredDAO.Data>> rlcd = ques.credDAO().readNS(trans, ns);
2564                     
2565             if (rlcd.isOK()) {
2566                 if (!rlcd.isEmpty()) {
2567                     return mapper.cred(rlcd.value, users);
2568                 }
2569                 return Result.ok(users);        
2570             } else {
2571                 return Result.err(rlcd);
2572             }
2573         } finally {
2574             tt.done();
2575         }
2576             
2577     }
2578
2579     @ApiDoc(   
2580             method = GET,  
2581             path = "/authn/creds/id/:ns",
2582             params = {"id|string|true"},
2583             expectedCode = 200,
2584             errorCodes = {403,404,406}, 
2585             text = { "Return all IDs in for ID"
2586                     ,"(because IDs are multiple, due to multiple Expiration Dates)"
2587                      }
2588             )
2589     @Override
2590     public Result<USERS> getCredsByID(AuthzTrans trans, String id) {
2591         final Validator v = new ServiceValidator();
2592         if (v.nullOrBlank("ID",id).err()) {
2593             return Result.err(Status.ERR_BadData,v.errs());
2594         }
2595         
2596         String ns = Question.domain2ns(id);
2597         // check if user is allowed to view NS
2598         Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2599         if (rnd.notOK()) {
2600             return Result.err(rnd); 
2601         }
2602         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2603         if (rnd.notOK()) {
2604             return Result.err(rnd); 
2605         }
2606     
2607         TimeTaken tt = trans.start("MAP Creds by ID to Creds", Env.SUB);
2608         try {            
2609             USERS users = mapper.newInstance(API.USERS);
2610             Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, id);
2611                     
2612             if (rlcd.isOK()) {
2613                 if (!rlcd.isEmpty()) {
2614                     return mapper.cred(rlcd.value, users);
2615                 }
2616                 return Result.ok(users);        
2617             } else {
2618                 return Result.err(rlcd);
2619             }
2620         } finally {
2621             tt.done();
2622         }
2623             
2624     }
2625
2626     @ApiDoc(   
2627             method = GET,  
2628             path = "/authn/certs/id/:id",
2629             params = {"id|string|true"},
2630             expectedCode = 200,
2631             errorCodes = {403,404,406}, 
2632             text = { "Return Cert Info for ID"
2633                    }
2634             )
2635     @Override
2636     public Result<CERTS> getCertInfoByID(AuthzTrans trans, HttpServletRequest req, String id) {
2637         TimeTaken tt = trans.start("Get Cert Info by ID", Env.SUB);
2638         try {            
2639             CERTS certs = mapper.newInstance(API.CERTS);
2640             Result<List<CertDAO.Data>> rlcd = ques.certDAO().readID(trans, id);
2641                     
2642             if (rlcd.isOK()) {
2643                 if (!rlcd.isEmpty()) {
2644                     return mapper.cert(rlcd.value, certs);
2645                 }
2646                 return Result.ok(certs);        
2647             } else { 
2648                 return Result.err(rlcd);
2649             }
2650         } finally {
2651             tt.done();
2652         }
2653
2654     }
2655
2656     @ApiDoc( 
2657             method = PUT,  
2658             path = "/authn/cred",
2659             params = {},
2660             expectedCode = 200,
2661             errorCodes = {300,403,404,406}, 
2662             text = { "Reset a Credential Password. If multiple credentials exist for this",
2663                         "ID, you will need to specify which entry you are resetting in the",
2664                         "CredRequest object"
2665                      }
2666             )
2667     @Override
2668     public Result<Void> resetUserCred(final AuthzTrans trans, REQUEST from) {
2669         final String cmdDescription = "Update User Credential";
2670         TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2671         try {
2672             Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2673             if (rcred.isOKhasData()) {
2674                 rcred = ques.userCredSetup(trans, rcred.value);
2675     
2676                 final ServiceValidator v = new ServiceValidator();
2677                 
2678                 if (v.cred(trans, trans.org(),rcred,false).err()) {// Note: Creates have stricter Validations 
2679                     return Result.err(Status.ERR_BadData,v.errs());
2680                 }
2681                 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, rcred.value.id);
2682                 if (rlcd.notOKorIsEmpty()) {
2683                     return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2684                 } 
2685                 
2686                 MayChange mc = new MayChangeCred(trans, rcred.value,MayChangeCred.RESET);
2687                 Result<?> rmc = mc.mayChange(); 
2688                 if (rmc.notOK()) {
2689                     return Result.err(rmc);
2690                 }
2691                 
2692                 List<CredDAO.Data> lcdd = filterList(rlcd.value,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
2693                 
2694                 Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, lcdd, MayChangeCred.RESET);
2695                 if (ri.notOK()) {
2696                     return Result.err(ri);
2697                 }
2698                 int entry = ri.value;
2699     
2700                 
2701                 final CredDAO.Data cred = rcred.value;
2702                 
2703                 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false,
2704                 new Mapper.Memo() {
2705                     @Override
2706                     public String get() {
2707                         return cmdDescription + " [" + 
2708                             cred.id + '|' 
2709                             + cred.type + '|' 
2710                             + cred.expires + ']';
2711                     }
2712                 },
2713                 mc);
2714                 
2715                 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rcred.value.ns);
2716                 if (nsr.notOKorIsEmpty()) {
2717                     return Result.err(nsr);
2718                 }
2719     
2720                 switch(fd.status) {
2721                     case OK:
2722                         Result<String> rfc = func.createFuture(trans, fd.value, 
2723                                 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2724                                 trans.user(), nsr.value.get(0), FUTURE_OP.U);
2725                         if (rfc.isOK()) {
2726                             return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s]",
2727                                     rcred.value.id,
2728                                     Integer.toString(rcred.value.type),
2729                                     rcred.value.expires.toString());
2730                         } else { 
2731                             return Result.err(rfc);
2732                         }
2733                     case Status.ACC_Now:
2734                         Result<?>udr = null;
2735                         // If we are Resetting Password on behalf of someone else (am not the Admin)
2736                         //  use TempPassword Expiration time.
2737                         Expiration exp;
2738                         if (ques.isAdmin(trans, trans.user(), nsr.value.get(0).name)) {
2739                             exp = Expiration.Password;
2740                         } else {
2741                             exp = Expiration.TempPassword;
2742                         }
2743                         
2744                         Organization org = trans.org();
2745                         CredDAO.Data current = rlcd.value.get(entry);
2746                         // If user resets password in same day, we will have a primary key conflict, so subtract 1 day
2747                         if (current.expires.equals(rcred.value.expires) 
2748                                     && rlcd.value.get(entry).type==rcred.value.type) {
2749                             GregorianCalendar gc = org.expiration(null, exp,rcred.value.id);
2750                             gc = Chrono.firstMomentOfDay(gc);
2751                             gc.set(GregorianCalendar.HOUR_OF_DAY, org.startOfDay());                        
2752                             rcred.value.expires = new Date(gc.getTimeInMillis() - DAY_IN_MILLIS);
2753                         } else {
2754                             rcred.value.expires = org.expiration(null,exp).getTime();
2755                         }
2756
2757                         udr = ques.credDAO().create(trans, rcred.value);
2758                         if (udr.isOK()) {
2759                             udr = ques.credDAO().delete(trans, rlcd.value.get(entry),false);
2760                         }
2761                         if (udr.isOK()) {
2762                             return Result.ok();
2763                         }
2764     
2765                         return Result.err(udr);
2766                     default:
2767                         return Result.err(fd);
2768                 }
2769             } else {
2770                 return Result.err(rcred);
2771             }
2772         } finally {
2773             tt.done();
2774         }
2775     }
2776
2777     @ApiDoc( 
2778             method = PUT,  
2779             path = "/authn/cred/:days",
2780             params = {"days|string|true"},
2781             expectedCode = 200,
2782             errorCodes = {300,403,404,406}, 
2783             text = { "Extend a Credential Expiration Date. The intention of this API is",
2784                         "to avoid an outage in PROD due to a Credential expiring before it",
2785                         "can be configured correctly. Measures are being put in place ",
2786                         "so that this is not abused."
2787                      }
2788             )
2789     @Override
2790     public Result<Void> extendUserCred(final AuthzTrans trans, REQUEST from, String days) {
2791         TimeTaken tt = trans.start("Extend User Credential", Env.SUB);
2792         try {
2793             Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2794             Organization org = trans.org();
2795             final ServiceValidator v = new ServiceValidator();
2796             if (v.notOK(cred).err() || 
2797                v.nullOrBlank(cred.value.id, "Invalid ID").err() ||
2798                v.user(org,cred.value.id).err())  {
2799                  return Result.err(Status.ERR_BadData,v.errs());
2800             }
2801             
2802             try {
2803                 String reason;
2804                 if ((reason=org.validate(trans, Policy.MAY_EXTEND_CRED_EXPIRES, new CassExecutor(trans,func)))!=null) {
2805                     return Result.err(Status.ERR_Policy,reason);
2806                 }
2807             } catch (Exception e) {
2808                 String msg;
2809                 trans.error().log(e, msg="Could not contact Organization for User Validation");
2810                 return Result.err(Status.ERR_Denied, msg);
2811             }
2812     
2813             // Get the list of Cred Entries
2814             Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, cred.value.id);
2815             if (rlcd.notOKorIsEmpty()) {
2816                 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2817             }
2818             
2819             // Only Passwords can be extended
2820             List<CredDAO.Data> lcdd = filterList(rlcd.value,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
2821
2822             //Need to do the "Pick Entry" mechanism
2823             // Note, this sorts
2824             Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, lcdd, MayChangeCred.EXTEND);
2825             if (ri.notOK()) {
2826                 return Result.err(ri);
2827             }
2828
2829             CredDAO.Data found = lcdd.get(ri.value);
2830             CredDAO.Data cd = cred.value;
2831             // Copy over the cred
2832             cd.id = found.id;
2833             cd.cred = found.cred;
2834             cd.other = found.other;
2835             cd.type = found.type;
2836             cd.ns = found.ns;
2837             cd.notes = "Extended";
2838             cd.tag = found.tag;
2839             cd.expires = org.expiration(null, Expiration.ExtendPassword,days).getTime();
2840             if(cd.expires.before(found.expires)) {
2841                 return Result.err(Result.ERR_BadData,String.format("Credential's expiration date is more than %s days in the future",days));
2842             }
2843             
2844             cred = ques.credDAO().create(trans, cd);
2845             if (cred.isOK()) {
2846                 return Result.ok();
2847             }
2848             return Result.err(cred);
2849         } finally {
2850             tt.done();
2851         }
2852     }    
2853
2854     @ApiDoc( 
2855                 method = DELETE,  
2856                 path = "/authn/cred",
2857                 params = {},
2858                 expectedCode = 200,
2859                 errorCodes = {300,403,404,406}, 
2860                 text = { "Delete a Credential. If multiple credentials exist for this",
2861                         "ID, you will need to specify which entry you are deleting in the",
2862                         "CredRequest object."
2863                          }
2864                 )
2865         @Override
2866         public Result<Void> deleteUserCred(AuthzTrans trans, REQUEST from)  {
2867             final Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2868             final Validator v = new ServiceValidator();
2869             if (v.nullOrBlank("cred", cred.value.id).err()) {
2870                 return Result.err(Status.ERR_BadData,v.errs());
2871             }
2872
2873             MayChange mc = new MayChangeCred(trans,cred.value,MayChangeCred.DELETE);
2874             Result<?> rmc = mc.mayChange(); 
2875             if (rmc.notOK()) {
2876                 return Result.err(rmc);
2877             }
2878             
2879             boolean doForce = trans.requested(force);
2880             Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, cred.value.id);
2881             if (rlcd.notOKorIsEmpty()) {
2882                 // Empty Creds should not have user_roles.
2883                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, cred.value.id);
2884                 if (rlurd.isOKhasData()) {
2885                     for (UserRoleDAO.Data data : rlurd.value) {
2886                         ques.userRoleDAO().delete(trans, data, false);
2887                     }
2888                 }
2889                 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2890             }
2891             boolean isLastCred = rlcd.value.size()==1;
2892             
2893             int entry;
2894             CredRequest cr = (CredRequest)from;
2895             if(isLastCred) {
2896                 if(cr.getEntry()==null || "1".equals(cr.getEntry())) {
2897                         entry = 0;
2898                 } else {
2899                     return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2900                 }
2901             } else {
2902                     entry = -1;
2903                 int fentry = entry;
2904                     if(cred.value.type==CredDAO.FQI) {
2905                         entry = -1;
2906                         for(CredDAO.Data cdd : rlcd.value) {
2907                                 ++fentry;
2908                                 if(cdd.type == CredDAO.FQI) {
2909                                         entry = fentry;
2910                                         break; 
2911                                 }
2912                         }
2913                     } else {
2914                             if (!doForce) {
2915                                 if (rlcd.value.size() > 1) {
2916                                     String inputOption = cr.getEntry();
2917                                     if (inputOption == null) {
2918                                         List<CredDAO.Data> list = filterList(rlcd.value,CredDAO.BASIC_AUTH,CredDAO.BASIC_AUTH_SHA256,CredDAO.CERT_SHA256_RSA);
2919                                         String message = selectCredFromList(list, MayChangeCred.DELETE);
2920                                         Object[] variables = buildVariables(list);
2921                                         return Result.err(Status.ERR_ChoiceNeeded, message, variables);
2922                                     } else {
2923                                         try {
2924                                             if (inputOption.length()>5) { // should be a date
2925                                                 Date d = Chrono.xmlDatatypeFactory.newXMLGregorianCalendar(inputOption).toGregorianCalendar().getTime();
2926                                                 for (CredDAO.Data cd : rlcd.value) {
2927                                                         ++fentry;
2928                                                     if (cd.type.equals(cr.getType()) && cd.expires.equals(d)) {
2929                                                         entry = fentry;
2930                                                         break;
2931                                                     }
2932                                                 }
2933                                             } else {
2934                                                 entry = Integer.parseInt(inputOption) - 1;
2935                                                 int count = 0;
2936                                                 for (CredDAO.Data cd : rlcd.value) {
2937                                                         if(cd.type!=CredDAO.BASIC_AUTH && cd.type!=CredDAO.BASIC_AUTH_SHA256 && cd.type!=CredDAO.CERT_SHA256_RSA) {
2938                                                                 ++entry;
2939                                                         }
2940                                                         if(++count>entry) {
2941                                                                 break;
2942                                                         }
2943                                                 }
2944                                             }
2945                                         } catch (NullPointerException e) {
2946                                             return Result.err(Status.ERR_BadData, "Invalid Date Format for Entry");
2947                                         } catch (NumberFormatException e) {
2948                                             return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2949                                         }
2950                                     }
2951                                     isLastCred = (entry==-1)?true:false;
2952                                 } else {
2953                                     isLastCred = true;
2954                                 }
2955                                 if (entry < -1 || entry >= rlcd.value.size()) {
2956                                     return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2957                                 }
2958                             }
2959                     }
2960             }
2961             
2962             Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from,cred.value,false,
2963                 () -> "Delete Credential [" +
2964                     cred.value.id +
2965                     ']',
2966                 mc);
2967         
2968             Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, cred.value.ns);
2969             if (nsr.notOKorIsEmpty()) {
2970                 return Result.err(nsr);
2971             }
2972         
2973             switch(fd.status) {
2974                 case OK:
2975                     Result<String> rfc = func.createFuture(trans, fd.value, cred.value.id,
2976                             trans.user(), nsr.value.get(0), FUTURE_OP.D);
2977         
2978                     if (rfc.isOK()) {
2979                         return Result.err(Status.ACC_Future, "Credential Delete [%s] is saved for future processing",cred.value.id);
2980                     } else { 
2981                         return Result.err(rfc);
2982                     }
2983                 case Status.ACC_Now:
2984                     Result<?>udr = null;
2985                     if (!trans.requested(force)) {
2986                         if (entry<0 || entry >= rlcd.value.size()) {
2987                                 if(cred.value.type==CredDAO.FQI) {
2988                                         return Result.err(Status.ERR_BadData,"FQI does not exist");
2989                                 } else {
2990                                         return Result.err(Status.ERR_BadData,"Invalid Choice [" + entry + "] chosen for Delete [%s] is saved for future processing",cred.value.id);
2991                                 }
2992                         }
2993                         udr = ques.credDAO().delete(trans, rlcd.value.get(entry),false);
2994                     } else {
2995                         for (CredDAO.Data curr : rlcd.value) {
2996                             udr = ques.credDAO().delete(trans, curr, false);
2997                             if (udr.notOK()) {
2998                                 return Result.err(udr);
2999                             }
3000                         }
3001                     }
3002                     if (isLastCred) {
3003                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, cred.value.id);
3004                         if (rlurd.isOK()) {
3005                             for (UserRoleDAO.Data data : rlurd.value) {
3006                                 ques.userRoleDAO().delete(trans, data, false);
3007                             }
3008                         }
3009                     }
3010                     if (udr==null) {
3011                         return Result.err(Result.ERR_NotFound,"No User Data found");
3012                     }
3013                     if (udr.isOK()) {
3014                         return Result.ok();
3015                     }
3016                     return Result.err(udr);
3017                 default:
3018                     return Result.err(fd);
3019             }
3020         
3021         }
3022
3023         /*
3024          * Codify the way to get Either Choice Needed or actual Integer from Credit Request
3025          */
3026         private Result<Integer> selectEntryIfMultiple(final CredRequest cr, List<CredDAO.Data> lcd, String action) {
3027             int entry = 0;
3028             if (lcd.size() > 1) {
3029                 String inputOption = cr.getEntry();
3030                 if (inputOption == null) {
3031                     String message = selectCredFromList(lcd, action);
3032                     Object[] variables = buildVariables(lcd);
3033                     return Result.err(Status.ERR_ChoiceNeeded, message, variables);
3034                 } else {
3035                         if(MayChangeCred.EXTEND.equals(action)) {
3036                                 // might be Tag
3037                                 if(inputOption.length()>4) { //Tag is at least 12
3038                                         int e = 0;
3039                                         CredDAO.Data last = null;
3040                                         int lastIdx = -1;
3041                                         for(CredDAO.Data cdd : lcd) {
3042                                                 if(inputOption.equals(cdd.tag)) {
3043                                                         if(last==null) {
3044                                                                 last = cdd;
3045                                                                 lastIdx = e;
3046                                                         } else {
3047                                                                 if(last.expires.before(cdd.expires)) {
3048                                                                         last = cdd;
3049                                                                         lastIdx = e;
3050                                                                 }
3051                                                         }
3052                                                 }
3053                                                 ++e;
3054                                         }
3055                                         if(last!=null) {
3056                                                 return Result.ok(lastIdx);
3057                                         }
3058                                         return Result.err(Status.ERR_BadData, "User chose unknown Tag");
3059                                 }
3060                         }
3061                     entry = Integer.parseInt(inputOption) - 1;
3062                 }
3063                 if (entry < 0 || entry >= lcd.size()) {
3064                     return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
3065                 }
3066             }
3067             return Result.ok(entry);
3068         }
3069
3070         private List<CredDAO.Data> filterList(List<CredDAO.Data> orig, Integer ... types) {
3071         List<CredDAO.Data> rv = new ArrayList<>();
3072         for(CredDAO.Data cdd : orig) {
3073                 if(cdd!=null) {
3074                         for(int t : types) {
3075                                 if(t==cdd.type) {
3076                                         rv.add(cdd);
3077                                 }
3078                         }
3079                 }
3080         }
3081         Collections.sort(rv, (o1,o2) -> {
3082                 if(o1.type==o2.type) {
3083                         return o1.expires.compareTo(o2.expires);
3084                 } else {
3085                         return o1.type.compareTo(o2.type);
3086                 }
3087         });
3088                 return rv;
3089         }
3090
3091         private String[] buildVariables(List<CredDAO.Data> value) {
3092         String [] vars = new String[value.size()];
3093         CredDAO.Data cdd;
3094         
3095         for (int i = 0; i < value.size(); i++) {
3096                 cdd = value.get(i);
3097                 vars[i] = cdd.id + TWO_SPACE + Define.getCredType(cdd.type) + TWO_SPACE + Chrono.niceUTCStamp(cdd.expires) + TWO_SPACE + cdd.tag;
3098         }
3099         return vars;
3100     }
3101     
3102     private String selectCredFromList(List<CredDAO.Data> value, String action) {
3103         StringBuilder errMessage = new StringBuilder();
3104         String userPrompt = MayChangeCred.DELETE.equals(action)?
3105                         "Select which cred to delete (set force=true to delete all):":
3106                         "Select which cred to " + action + ':';
3107         int numSpaces = value.get(0).id.length() - "Id".length();
3108         
3109         errMessage.append(userPrompt + '\n');
3110         errMessage.append("        ID");
3111         for (int i = 0; i < numSpaces; i++) {
3112             errMessage.append(' ');
3113         }
3114         errMessage.append("  Type  Expires               Tag " + '\n');
3115         for (int i=0;i<value.size();++i) {
3116             errMessage.append("    %s\n");
3117         }
3118         if(MayChangeCred.EXTEND.equals(action)) {
3119             errMessage.append("Run same command again with chosen entry or Tag as last parameter");
3120         } else {
3121                 errMessage.append("Run same command again with chosen entry as last parameter");
3122         }
3123         return errMessage.toString();
3124         
3125     }
3126
3127     @Override
3128     public Result<Date> doesCredentialMatch(AuthzTrans trans, REQUEST credReq) {
3129         TimeTaken tt = trans.start("Does Credential Match", Env.SUB);
3130         try {
3131             // Note: Mapper assigns RAW type
3132             Result<CredDAO.Data> data = mapper.cred(trans, credReq,false);
3133             if (data.notOKorIsEmpty()) {
3134                 return Result.err(data);
3135             }
3136             CredDAO.Data cred = data.value;    // of the Mapped Cred
3137             if (cred.cred==null) {
3138                 return Result.err(Result.ERR_BadData,"No Password");
3139             } else {
3140                 return ques.doesUserCredMatch(trans, cred.id, cred.cred.array());
3141             }
3142
3143         } catch (DAOException e) {
3144             trans.error().log(e,"Error looking up cred");
3145             return Result.err(Status.ERR_Denied,"Credential does not match");
3146         } finally {
3147             tt.done();
3148         }
3149     }
3150
3151     @ApiDoc( 
3152             method = POST,  
3153             path = "/authn/validate",
3154             params = {},
3155             expectedCode = 200,
3156             errorCodes = { 403 }, 
3157             text = { "Validate a Credential given a Credential Structure.  This is a more comprehensive validation, can "
3158                     + "do more than BasicAuth as Credential types exp" }
3159             )
3160     @Override
3161     public Result<Date> validateBasicAuth(AuthzTrans trans, String basicAuth) {
3162         //TODO how to make sure people don't use this in browsers?  Do we care?
3163         TimeTaken tt = trans.start("Validate Basic Auth", Env.SUB);
3164         try {
3165             BasicPrincipal bp = new BasicPrincipal(basicAuth,trans.org().getRealm());
3166             Result<Date> rq = ques.doesUserCredMatch(trans, bp.getName(), bp.getCred());
3167             // Note: Only want to log problem, don't want to send back to end user
3168             if (rq.isOK()) {
3169                 return rq;
3170             } else {
3171                 trans.audit().log(rq.errorString());
3172             }
3173         } catch (Exception e) {
3174             trans.warn().log(e);
3175         } finally {
3176             tt.done();
3177         }
3178         return Result.err(Status.ERR_Denied,"Bad Basic Auth");
3179     }
3180
3181 @ApiDoc( 
3182                 method = GET,  
3183                 path = "/authn/basicAuth",
3184                 params = {},
3185                 expectedCode = 200,
3186                 errorCodes = { 403 }, 
3187                 text = { "!!!! DEPRECATED without X509 Authentication STOP USING THIS API BY DECEMBER 2017, or use Certificates !!!!\n" 
3188                         + "Use /authn/validate instead\n"
3189                         + "Note: Validate a Password using BasicAuth Base64 encoded Header. This HTTP/S call is intended as a fast"
3190                         + " User/Password lookup for Security Frameworks, and responds 200 if it passes BasicAuth "
3191                     + "security, and 403 if it does not." }
3192                 )
3193         private void basicAuth() {
3194             // This is a place holder for Documentation.  The real BasicAuth API does not call Service.
3195         }
3196
3197 /***********************************
3198  * USER-ROLE 
3199  ***********************************/
3200     @ApiDoc( 
3201             method = POST,  
3202             path = "/authz/userRole",
3203             params = {},
3204             expectedCode = 201,
3205             errorCodes = {403,404,406,409}, 
3206             text = { "Create a UserRole relationship (add User to Role)",
3207                      "A UserRole is an object Representation of membership of a Role for limited time.",
3208                      "If a shorter amount of time for Role ownership is required, use the 'End' field.",
3209                      "** Note: Owners of Namespaces will be required to revalidate users in these roles ",
3210                      "before Expirations expire.  Namespace owners will be notified by email."
3211                    }
3212             )
3213     @Override
3214     public Result<Void> createUserRole(final AuthzTrans trans, REQUEST from) {
3215         TimeTaken tt = trans.start("Create UserRole", Env.SUB);
3216         try {
3217             Result<UserRoleDAO.Data> urr = mapper.userRole(trans, from);
3218             if (urr.notOKorIsEmpty()) {
3219                 return Result.err(urr);
3220             }
3221             final UserRoleDAO.Data userRole = urr.value;
3222             
3223             final ServiceValidator v = new ServiceValidator();
3224             if (v.user_role(trans.user(),userRole).err() ||
3225                v.user(trans.org(), userRole.user).err()) {
3226                 return Result.err(Status.ERR_BadData,v.errs());
3227             }
3228
3229
3230              
3231             // Check if user can change first
3232             Result<FutureDAO.Data> fd = mapper.future(trans,UserRoleDAO.TABLE,from,urr.value,true, // may request Approvals
3233                 () -> "Add User [" + userRole.user + "] to Role [" +
3234                         userRole.role +
3235                         ']',
3236                 new MayChange() {
3237                     private Result<NsDAO.Data> nsd;
3238                     @Override
3239                     public Result<?> mayChange() {
3240                         if(urr.value.role.startsWith(urr.value.user)) {
3241                                 return Result.ok((NsDAO.Data)null);
3242                         }
3243                         if (nsd==null) {
3244                             RoleDAO.Data r = RoleDAO.Data.decode(userRole);
3245                             nsd = ques.mayUser(trans, trans.user(), r, Access.write);
3246                         }
3247                         return nsd;
3248                     }
3249                 });
3250             
3251             NsDAO.Data ndd;
3252             if(userRole.role.startsWith(userRole.user)) {
3253                 userRole.ns=userRole.user;
3254                 userRole.rname="user";
3255                 ndd = null;
3256             } else {
3257                     Result<NsDAO.Data> nsr = ques.deriveNs(trans, userRole.role);
3258                     if (nsr.notOK()) {
3259                         return Result.err(nsr);
3260                     }
3261                     ndd = nsr.value;
3262             }
3263
3264             switch(fd.status) {
3265                 case OK:
3266                     Result<String> rfc = func.createFuture(trans, fd.value, userRole.user+'|'+userRole.ns + '.' + userRole.rname, 
3267                             userRole.user, ndd, FUTURE_OP.C);
3268                     if (rfc.isOK()) {
3269                         return Result.err(Status.ACC_Future, "UserRole [%s - %s.%s] is saved for future processing",
3270                                 userRole.user,
3271                                 userRole.ns,
3272                                 userRole.rname);
3273                     } else { 
3274                         return Result.err(rfc);
3275                     }
3276                 case Status.ACC_Now:
3277                     return func.addUserRole(trans, userRole);
3278                 default:
3279                     return Result.err(fd);
3280             }
3281         } finally {
3282             tt.done();
3283         }
3284     }
3285     
3286         /**
3287          * getUserRolesByRole
3288          */
3289         @ApiDoc(
3290                 method = GET,
3291                 path = "/authz/userRoles/role/:role",
3292                 params = {"role|string|true"},
3293                 expectedCode = 200,
3294                 errorCodes = {404,406},
3295                 text = { "List all Users that are attached to Role specified in :role",
3296                         }
3297                )
3298         @Override
3299         public Result<USERROLES> getUserRolesByRole(AuthzTrans trans, String role) {
3300             final Validator v = new ServiceValidator();
3301             if (v.nullOrBlank("Role",role).err()) {
3302                 return Result.err(Status.ERR_BadData,v.errs());
3303             }
3304             
3305             Result<RoleDAO.Data> rrdd;
3306             rrdd = RoleDAO.Data.decode(trans,ques,role);
3307             if (rrdd.notOK()) {
3308                 return Result.err(rrdd);
3309             }
3310             // May Requester see result?
3311             Result<NsDAO.Data> ns = ques.mayUser(trans,trans.user(), rrdd.value,Access.read);
3312             if (ns.notOK()) {
3313                 return Result.err(ns);
3314             }
3315     
3316     //        boolean filter = true;        
3317     //        if (ns.value.isAdmin(trans.user()) || ns.value.isResponsible(trans.user()))
3318     //            filter = false;
3319             
3320             // Get list of roles per user, then add to Roles as we go
3321             HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3322             Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role);
3323             if (rlurd.isOK()) {
3324                 for (UserRoleDAO.Data data : rlurd.value) {
3325                     userSet.add(data);
3326                 }
3327             }
3328             
3329             @SuppressWarnings("unchecked")
3330             USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3331             // Checked for permission
3332             mapper.userRoles(trans, userSet, users);
3333             return Result.ok(users);
3334         }
3335         /**
3336          * getUserRolesByRole
3337          */
3338         @ApiDoc(
3339                 method = GET,
3340                 path = "/authz/userRoles/user/:user",
3341                 params = {"role|string|true"},
3342                 expectedCode = 200,
3343                 errorCodes = {404,406},
3344                 text = { "List all UserRoles for :user",
3345                         }
3346                )
3347         @Override
3348         public Result<USERROLES> getUserRolesByUser(AuthzTrans trans, String user) {
3349             final Validator v = new ServiceValidator();
3350             if (v.nullOrBlank("User",user).err()) {
3351                 return Result.err(Status.ERR_BadData,v.errs());
3352             }
3353             
3354             // Get list of roles per user, then add to Roles as we go
3355             Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, user);
3356             if (rlurd.notOK()) { 
3357                 return Result.err(rlurd);
3358             }
3359             
3360             /* Check for
3361              *   1) is User 
3362              *   2) is User's Supervisor
3363              *   3) Has special global access =read permission
3364              *   
3365              *   If none of the 3, then filter results to NSs in which Calling User has Ns.access * read
3366              */
3367             boolean mustFilter;
3368             String callingUser = trans.getUserPrincipal().getName();
3369             NsDAO.Data ndd = new NsDAO.Data();
3370
3371             if (user.equals(callingUser)) {
3372                 mustFilter = false;
3373             } else {
3374                 Organization org = trans.org();
3375                 try {
3376                     Identity orgID = org.getIdentity(trans, user);
3377                     Identity manager = orgID==null?null:orgID.responsibleTo();
3378                     if (orgID!=null && (manager!=null && callingUser.equals(manager.fullID()))) {
3379                         mustFilter = false;
3380                     } else if (ques.isGranted(trans, callingUser, ROOT_NS, Question.ACCESS, "*", Access.read.name())) {
3381                         mustFilter=false;
3382                     } else {
3383                         mustFilter = true;
3384                     }
3385                 } catch (OrganizationException e) {
3386                     trans.env().log(e);
3387                     mustFilter = true;
3388                 }
3389             }
3390             
3391             List<UserRoleDAO.Data> content;
3392             if (mustFilter) {
3393                 content = new ArrayList<>(rlurd.value.size()); // avoid multi-memory redos
3394                 
3395                 for (UserRoleDAO.Data data : rlurd.value) {
3396                     ndd.name=data.ns;
3397                     Result<Data> mur = ques.mayUser(trans, callingUser, ndd, Access.read);
3398                     if (mur.isOK()){
3399                         content.add(data);
3400                     }
3401                 }
3402                 
3403             } else {
3404                 content = rlurd.value;
3405             }
3406
3407
3408             @SuppressWarnings("unchecked")
3409             USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3410             // Checked for permission
3411             mapper.userRoles(trans, content, users);
3412             return Result.ok(users);
3413         }
3414
3415         
3416  
3417     
3418      @ApiDoc(
3419             method = GET,
3420             path = "/authz/userRole/extend/:user/:role",
3421             params = {    "user|string|true",
3422                         "role|string|true"
3423                     },
3424             expectedCode = 200,
3425             errorCodes = {403,404,406},
3426             text = { "Extend the Expiration of this User Role by the amount set by Organization",
3427                      "Requestor must be allowed to modify the role"
3428                     }
3429            )
3430     @Override
3431     public Result<Void> extendUserRole(AuthzTrans trans, String user, String role) {
3432         Organization org = trans.org();
3433         final ServiceValidator v = new ServiceValidator();
3434         if (v.user(org, user)
3435             .role(role)
3436             .err()) {
3437             return Result.err(Status.ERR_BadData,v.errs());
3438         }
3439     
3440         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3441         if (rrdd.notOK()) {
3442             return Result.err(rrdd);
3443         }
3444         
3445         Result<NsDAO.Data> rcr = ques.mayUser(trans, trans.user(), rrdd.value, Access.write);
3446         boolean mayNotChange;
3447         if ((mayNotChange = rcr.notOK()) && !trans.requested(future)) {
3448             return Result.err(rcr);
3449         }
3450         
3451         Result<List<UserRoleDAO.Data>> rr = ques.userRoleDAO().read(trans, user,role);
3452         if (rr.notOK()) {
3453             return Result.err(rr);
3454         }
3455         for (UserRoleDAO.Data userRole : rr.value) {
3456             if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3457                 FutureDAO.Data fto = new FutureDAO.Data();
3458                 fto.target=UserRoleDAO.TABLE;
3459                 fto.memo = "Extend User ["+userRole.user+"] in Role ["+userRole.role+"]";
3460                 GregorianCalendar now = new GregorianCalendar();
3461                 fto.start = now.getTime();
3462                 fto.expires = org.expiration(now, Expiration.Future).getTime();
3463                 try {
3464                     fto.construct = userRole.bytify();
3465                 } catch (IOException e) {
3466                     trans.error().log(e, "Error while bytifying UserRole for Future");
3467                     return Result.err(e);
3468                 }
3469
3470                 Result<String> rfc = func.createFuture(trans, fto, 
3471                         userRole.user+'|'+userRole.role, userRole.user, rcr.value, FUTURE_OP.U);
3472                 if (rfc.isOK()) {
3473                     return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3474                             userRole.user,
3475                             userRole.role);
3476                 } else {
3477                     return Result.err(rfc);
3478                 }
3479             } else {
3480                 return func.extendUserRole(trans, userRole, false);
3481             }
3482         }
3483         return Result.err(Result.ERR_NotFound,"This user and role doesn't exist");
3484     }
3485
3486     @ApiDoc( 
3487             method = DELETE,  
3488             path = "/authz/userRole/:user/:role",
3489             params = {    "user|string|true",
3490                         "role|string|true"
3491                     },
3492             expectedCode = 200,
3493             errorCodes = {403,404,406}, 
3494             text = { "Remove Role :role from User :user."
3495                    }
3496             )
3497     @Override
3498     public Result<Void> deleteUserRole(AuthzTrans trans, String usr, String role) {
3499         Validator val = new ServiceValidator();
3500         if (val.nullOrBlank("User", usr)
3501               .nullOrBlank("Role", role).err()) {
3502             return Result.err(Status.ERR_BadData, val.errs());
3503         }
3504
3505         boolean mayNotChange;
3506         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3507         if (rrdd.notOK()) {
3508             return Result.err(rrdd);
3509         }
3510         
3511         RoleDAO.Data rdd = rrdd.value;
3512         Result<NsDAO.Data> rns = ques.mayUser(trans, trans.user(), rdd, Access.write);
3513
3514         // Make sure we don't delete the last owner of valid NS
3515         if (rns.isOKhasData() && Question.OWNER.equals(rdd.name) && ques.countOwner(trans,rdd.ns)<=1) {
3516             return Result.err(Status.ERR_Denied,"You may not delete the last Owner of " + rdd.ns );
3517         }
3518         
3519         if (mayNotChange=rns.notOK()) {
3520             if (!trans.requested(future)) {
3521                 return Result.err(rns);
3522             }
3523         }
3524
3525         Result<List<UserRoleDAO.Data>> rulr;
3526         if ((rulr=ques.userRoleDAO().read(trans, usr, role)).notOKorIsEmpty()) {
3527             return Result.err(Status.ERR_UserRoleNotFound, "User [ "+usr+" ] is not "
3528                     + "Assigned to the Role [ " + role + " ]");
3529         }
3530
3531         UserRoleDAO.Data userRole = rulr.value.get(0);
3532         if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3533             FutureDAO.Data fto = new FutureDAO.Data();
3534             fto.target=UserRoleDAO.TABLE;
3535             fto.memo = "Remove User ["+userRole.user+"] from Role ["+userRole.role+"]";
3536             GregorianCalendar now = new GregorianCalendar();
3537             fto.start = now.getTime();
3538             fto.expires = trans.org().expiration(now, Expiration.Future).getTime();
3539
3540             Result<String> rfc = func.createFuture(trans, fto, 
3541                     userRole.user+'|'+userRole.role, userRole.user, rns.value, FUTURE_OP.D);
3542             if (rfc.isOK()) {
3543                 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing", 
3544                         userRole.user,
3545                         userRole.role);
3546             } else { 
3547                 return Result.err(rfc);
3548             }
3549         } else {
3550             return ques.userRoleDAO().delete(trans, rulr.value.get(0), false);
3551         }
3552     }
3553
3554     @ApiDoc( 
3555             method = GET,  
3556             path = "/authz/userRole/:user/:role",
3557             params = {"user|string|true",
3558                       "role|string|true"},
3559             expectedCode = 200,
3560             errorCodes = {403,404,406}, 
3561             text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3562                    }
3563             )
3564     @Override
3565     public Result<USERS> getUserInRole(AuthzTrans trans, String user, String role) {
3566         final Validator v = new ServiceValidator();
3567         if (v.role(role).nullOrBlank("User", user).err()) {
3568             return Result.err(Status.ERR_BadData,v.errs());
3569         }
3570
3571 //        Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3572 //        if (ns.notOK()) return Result.err(ns);
3573 //        
3574 //        Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3575         // May calling user see by virtue of the Role
3576         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3577         if (rrdd.notOK()) {
3578             return Result.err(rrdd);
3579         }
3580         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3581         if (rnd.notOK()) {
3582             return Result.err(rnd); 
3583         }
3584         
3585         HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3586         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readUserInRole(trans, user, role);
3587         if (rlurd.isOK()) {
3588             for (UserRoleDAO.Data data : rlurd.value) {
3589                 userSet.add(data);
3590             }
3591         }
3592         
3593         @SuppressWarnings("unchecked")
3594         USERS users = (USERS) mapper.newInstance(API.USERS);
3595         mapper.users(trans, userSet, users);
3596         return Result.ok(users);
3597     }
3598
3599     @ApiDoc( 
3600             method = GET,  
3601             path = "/authz/users/role/:role",
3602             params = {"user|string|true",
3603                       "role|string|true"},
3604             expectedCode = 200,
3605             errorCodes = {403,404,406}, 
3606             text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3607                    }
3608             )
3609     @Override
3610     public Result<USERS> getUsersByRole(AuthzTrans trans, String role) {
3611         final Validator v = new ServiceValidator();
3612         if (v.nullOrBlank("Role",role).err()) {
3613             return Result.err(Status.ERR_BadData,v.errs());
3614         }
3615
3616 //        Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3617 //        if (ns.notOK()) return Result.err(ns);
3618 //        
3619 //        Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3620         // May calling user see by virtue of the Role
3621         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3622         if (rrdd.notOK()) {
3623             return Result.err(rrdd);
3624         }
3625         
3626         boolean contactOnly = false;
3627         // Allow the request of any valid user to find the contact of the NS (Owner)
3628         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3629         if (rnd.notOK()) {
3630             if (Question.OWNER.equals(rrdd.value.name)) {
3631                 contactOnly = true;
3632             } else {
3633                 return Result.err(rnd);
3634             }
3635         }
3636         
3637         HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3638         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role);
3639         if (rlurd.isOK()) { 
3640             for (UserRoleDAO.Data data : rlurd.value) {
3641                 if (contactOnly) { //scrub data
3642                     // Can't change actual object, or will mess up the cache.
3643                     UserRoleDAO.Data scrub = new UserRoleDAO.Data();
3644                     scrub.ns = data.ns;
3645                     scrub.rname = data.rname;
3646                     scrub.role = data.role;
3647                     scrub.user = data.user;
3648                     userSet.add(scrub);
3649                 } else {
3650                     userSet.add(data);
3651                 }
3652             }
3653         }
3654         
3655         @SuppressWarnings("unchecked")
3656         USERS users = (USERS) mapper.newInstance(API.USERS);
3657         mapper.users(trans, userSet, users);
3658         return Result.ok(users);
3659     }
3660
3661     /**
3662      * getUsersByPermission
3663      */
3664     @ApiDoc(
3665             method = GET,
3666             path = "/authz/users/perm/:type/:instance/:action",
3667             params = {    "type|string|true",
3668                         "instance|string|true",
3669                         "action|string|true"
3670                     },
3671             expectedCode = 200,
3672             errorCodes = {404,406},
3673             text = { "List all Users that have Permission specified by :type :instance :action",
3674                     }
3675            )
3676     @Override
3677     public Result<USERS> getUsersByPermission(AuthzTrans trans, String type, String instance, String action) {
3678         final Validator v = new ServiceValidator();
3679         if (v.nullOrBlank("Type",type)
3680             .nullOrBlank("Instance",instance)
3681             .nullOrBlank("Action",action)            
3682             .err()) {
3683             return Result.err(Status.ERR_BadData,v.errs());
3684         }
3685
3686         Result<NsSplit> nss = ques.deriveNsSplit(trans, type);
3687         if (nss.notOK()) {
3688             return Result.err(nss);
3689         }
3690         
3691         Result<List<NsDAO.Data>> nsd = ques.nsDAO().read(trans, nss.value.ns);
3692         if (nsd.notOK()) {
3693             return Result.err(nsd);
3694         }
3695         
3696         boolean allInstance = ASTERIX.equals(instance);
3697         boolean allAction = ASTERIX.equals(action);
3698         // Get list of roles per Permission, 
3699         // Then loop through Roles to get Users
3700         // Note: Use Sets to avoid processing or responding with Duplicates
3701         Set<String> roleUsed = new HashSet<>();
3702         Set<UserRoleDAO.Data> userSet = new HashSet<>();
3703         
3704         if (!nss.isEmpty()) {
3705             Result<List<PermDAO.Data>> rlp = ques.permDAO().readByType(trans, nss.value.ns, nss.value.name);
3706             if (rlp.isOKhasData()) {
3707                 for (PermDAO.Data pd : rlp.value) {
3708                     if ((allInstance || pd.instance.equals(instance)) && 
3709                             (allAction || pd.action.equals(action))) {
3710                         if (ques.mayUser(trans, trans.user(),pd,Access.read).isOK()) {
3711                             for (String role : pd.roles) {
3712                                 if (!roleUsed.contains(role)) { // avoid evaluating Role many times
3713                                     roleUsed.add(role);
3714                                     Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role.replace('|', '.'));
3715                                     if (rlurd.isOKhasData()) {
3716                                         for (UserRoleDAO.Data urd : rlurd.value) {
3717                                             userSet.add(urd);
3718                                         }
3719                                     }
3720                                 }
3721                             }
3722                         }
3723                     }
3724                 }
3725             }
3726         }
3727         @SuppressWarnings("unchecked")
3728         USERS users = (USERS) mapper.newInstance(API.USERS);
3729         mapper.users(trans, userSet, users);
3730         return Result.ok(users);
3731     }
3732
3733 /***********************************
3734  * HISTORY 
3735  ***********************************/    
3736     @Override
3737     public Result<HISTORY> getHistoryByUser(final AuthzTrans trans, String user, final int[] yyyymm, final int sort) {    
3738         final Validator v = new ServiceValidator();
3739         if (v.nullOrBlank("User",user).err()) {
3740             return Result.err(Status.ERR_BadData,v.errs());
3741         }
3742
3743         Result<NsDAO.Data> rnd;
3744         // Users may look at their own data
3745          if (trans.user().equals(user)) {
3746                 // Users may look at their own data
3747          } else {
3748             int at = user.indexOf('@');
3749             if (at>=0 && trans.org().getRealm().equals(user.substring(at+1))) {
3750                 NsDAO.Data nsd  = new NsDAO.Data();
3751                 nsd.name = Question.domain2ns(user);
3752                 rnd = ques.mayUser(trans, trans.user(), nsd, Access.read);
3753                 if (rnd.notOK()) {
3754                     return Result.err(rnd);
3755                 }
3756             } else {
3757                 rnd = ques.validNSOfDomain(trans, user);
3758                 if (rnd.notOK()) {
3759                     return Result.err(rnd);
3760                 }
3761
3762                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3763                 if (rnd.notOK()) {
3764                     return Result.err(rnd);
3765                 }
3766             }
3767          }
3768         Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readByUser(trans, user, yyyymm);
3769         if (resp.notOK()) {
3770             return Result.err(resp);
3771         }
3772         return mapper.history(trans, resp.value,sort);
3773     }
3774
3775     @Override
3776     public Result<HISTORY> getHistoryByRole(AuthzTrans trans, String role, int[] yyyymm, final int sort) {
3777         final Validator v = new ServiceValidator();
3778         if (v.nullOrBlank("Role",role).err()) {
3779             return Result.err(Status.ERR_BadData,v.errs());
3780         }
3781
3782         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3783         if (rrdd.notOK()) {
3784             return Result.err(rrdd);
3785         }
3786         
3787         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
3788         if (rnd.notOK()) {
3789             return Result.err(rnd);
3790         }
3791         Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, role, "role", yyyymm); 
3792         if (resp.notOK()) {
3793             return Result.err(resp);
3794         }
3795         return mapper.history(trans, resp.value,sort);
3796     }
3797
3798     @Override
3799     public Result<HISTORY> getHistoryByPerm(AuthzTrans trans, String type, int[] yyyymm, final int sort) {
3800         final Validator v = new ServiceValidator();
3801         if (v.nullOrBlank("Type",type)
3802             .err()) {
3803             return Result.err(Status.ERR_BadData,v.errs());
3804         }
3805
3806         // May user see Namespace of Permission (since it's only one piece... we can't check for "is permission part of")
3807         Result<List<HistoryDAO.Data>> resp;
3808         if(type.startsWith(trans.user())) {
3809                 resp = ques.historyDAO().readBySubject(trans, type, "perm", yyyymm);
3810         } else {
3811             Result<NsDAO.Data> rnd = ques.deriveNs(trans,type);
3812                 if (rnd.notOK()) {
3813                     return Result.err(rnd);
3814                 }
3815                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3816                 if (rnd.notOK()) {
3817                     return Result.err(rnd);    
3818                 }
3819                 resp = ques.historyDAO().readBySubject(trans, type, "perm", yyyymm);
3820         }
3821         
3822         if (resp.notOK()) {
3823             return Result.err(resp);
3824         }
3825         return mapper.history(trans, resp.value,sort);
3826     }
3827
3828     @Override
3829     public Result<HISTORY> getHistoryByNS(AuthzTrans trans, String ns, int[] yyyymm, final int sort) {
3830         final Validator v = new ServiceValidator();
3831         if (v.nullOrBlank("NS",ns).err()) { 
3832             return Result.err(Status.ERR_BadData,v.errs());
3833         }
3834
3835         Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
3836         if (rnd.notOK()) {
3837             return Result.err(rnd);
3838         }
3839         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3840         if (rnd.notOK()) {
3841             return Result.err(rnd);    
3842         }
3843
3844         Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, ns, "ns", yyyymm);
3845         if (resp.notOK()) {
3846             return Result.err(resp);
3847         }
3848         return mapper.history(trans, resp.value,sort);
3849     }
3850
3851     @Override
3852     public Result<HISTORY> getHistoryBySubject(AuthzTrans trans, String subject, String target, int[] yyyymm, final int sort) {
3853         NsDAO.Data ndd = new NsDAO.Data();
3854         ndd.name = FQI.reverseDomain(subject);
3855         Result<Data> rnd = ques.mayUser(trans, trans.user(), ndd, Access.read);
3856         if (rnd.notOK()) {
3857             return Result.err(rnd);    
3858         }
3859
3860         Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, subject, target, yyyymm);
3861         if (resp.notOK()) {
3862             return Result.err(resp);
3863         }
3864         return mapper.history(trans, resp.value,sort);
3865     }
3866
3867 /***********************************
3868  * DELEGATE 
3869  ***********************************/
3870     @Override
3871     public Result<Void> createDelegate(final AuthzTrans trans, REQUEST base) {
3872         return createOrUpdateDelegate(trans, base, Question.Access.create);
3873     }
3874
3875     @Override
3876     public Result<Void> updateDelegate(AuthzTrans trans, REQUEST base) {
3877         return createOrUpdateDelegate(trans, base, Question.Access.write);
3878     }
3879
3880
3881     private Result<Void> createOrUpdateDelegate(final AuthzTrans trans, REQUEST base, final Access access) {
3882         final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3883         final ServiceValidator v = new ServiceValidator();
3884         if (v.delegate(trans.org(),rd).err()) { 
3885             return Result.err(Status.ERR_BadData,v.errs());
3886         }
3887
3888         final DelegateDAO.Data dd = rd.value;
3889         
3890         Result<List<DelegateDAO.Data>> ddr = ques.delegateDAO().read(trans, dd);
3891         if (access==Access.create && ddr.isOKhasData()) {
3892             return Result.err(Status.ERR_ConflictAlreadyExists, "[%s] already delegates to [%s]", dd.user, ddr.value.get(0).delegate);
3893         } else if (access!=Access.create && ddr.notOKorIsEmpty()) { 
3894             return Result.err(Status.ERR_NotFound, "[%s] does not have a Delegate Record to [%s].",dd.user,access.name());
3895         }
3896         Result<Void> rv = ques.mayUser(trans, dd, access);
3897         if (rv.notOK()) {
3898             return rv;
3899         }
3900         
3901         Result<FutureDAO.Data> fd = mapper.future(trans,DelegateDAO.TABLE,base, dd, false,
3902             () -> {
3903                 StringBuilder sb = new StringBuilder();
3904                 sb.append(access.name());
3905                 sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
3906                 sb.append("Delegate ");
3907                 sb.append(access==Access.create?"[":"to [");
3908                 sb.append(rd.value.delegate);
3909                 sb.append("] for [");
3910                 sb.append(rd.value.user);
3911                 sb.append(']');
3912                 return sb.toString();
3913             },
3914             () -> {
3915                 return Result.ok(); // Validate in code above
3916             });
3917         
3918         switch(fd.status) {
3919             case OK:
3920                 Result<String> rfc = func.createFuture(trans, fd.value, 
3921                         dd.user, trans.user(),null, access==Access.create?FUTURE_OP.C:FUTURE_OP.U);
3922                 if (rfc.isOK()) { 
3923                     return Result.err(Status.ACC_Future, "Delegate for [%s]",
3924                             dd.user);
3925                 } else { 
3926                     return Result.err(rfc);
3927                 }
3928             case Status.ACC_Now:
3929                 if (access==Access.create) {
3930                     Result<DelegateDAO.Data> rdr = ques.delegateDAO().create(trans, dd);
3931                     if (rdr.isOK()) {
3932                         return Result.ok();
3933                     } else {
3934                         return Result.err(rdr);
3935                     }
3936                 } else {
3937                     return ques.delegateDAO().update(trans, dd);
3938                 }
3939             default:
3940                 return Result.err(fd);
3941         }
3942     }
3943
3944     @Override
3945     public Result<Void> deleteDelegate(AuthzTrans trans, REQUEST base) {
3946         final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3947         final Validator v = new ServiceValidator();
3948         if (v.notOK(rd).nullOrBlank("User", rd.value.user).err()) {
3949             return Result.err(Status.ERR_BadData,v.errs());
3950         }
3951         
3952         Result<List<DelegateDAO.Data>> ddl;
3953         if ((ddl=ques.delegateDAO().read(trans, rd.value)).notOKorIsEmpty()) {
3954             return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3955         }
3956         final DelegateDAO.Data dd = ddl.value.get(0);
3957         Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3958         if (rv.notOK()) {
3959             return rv;
3960         }
3961         
3962         return ques.delegateDAO().delete(trans, dd, false);
3963     }
3964
3965     @Override
3966     public Result<Void> deleteDelegate(AuthzTrans trans, String userName) {
3967         DelegateDAO.Data dd = new DelegateDAO.Data();
3968         final Validator v = new ServiceValidator();
3969         if (v.nullOrBlank("User", userName).err()) {
3970             return Result.err(Status.ERR_BadData,v.errs());
3971         }
3972         dd.user = userName;
3973         Result<List<DelegateDAO.Data>> ddl;
3974         if ((ddl=ques.delegateDAO().read(trans, dd)).notOKorIsEmpty()) {
3975             return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3976         }
3977         dd = ddl.value.get(0);
3978         Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3979         if (rv.notOK()) {
3980             return rv;
3981         }
3982         
3983         return ques.delegateDAO().delete(trans, dd, false);
3984     }
3985     
3986     @Override
3987     public Result<DELGS> getDelegatesByUser(AuthzTrans trans, String user) {
3988         final Validator v = new ServiceValidator();
3989         if (v.nullOrBlank("User", user).err()) {
3990             return Result.err(Status.ERR_BadData,v.errs());
3991         }
3992
3993         DelegateDAO.Data ddd = new DelegateDAO.Data();
3994         ddd.user = user;
3995         ddd.delegate = null;
3996         Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3997         if (rv.notOK()) {
3998             return Result.err(rv);
3999         }
4000         
4001         TimeTaken tt = trans.start("Get delegates for a user", Env.SUB);
4002
4003         Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO().read(trans, user);
4004         try {
4005             if (dbDelgs.isOKhasData()) {
4006                 return mapper.delegate(dbDelgs.value);
4007             } else {
4008                 return Result.err(Status.ERR_DelegateNotFound,"No Delegate found for [%s]",user);
4009             }
4010         } finally {
4011             tt.done();
4012         }        
4013     }
4014
4015     @Override
4016     public Result<DELGS> getDelegatesByDelegate(AuthzTrans trans, String delegate) {
4017         final Validator v = new ServiceValidator();
4018         if (v.nullOrBlank("Delegate", delegate).err()) {
4019             return Result.err(Status.ERR_BadData,v.errs());
4020         }
4021
4022         DelegateDAO.Data ddd = new DelegateDAO.Data();
4023         ddd.user = delegate;
4024         Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
4025         if (rv.notOK()) {
4026             return Result.err(rv);
4027         }
4028
4029         TimeTaken tt = trans.start("Get users for a delegate", Env.SUB);
4030
4031         Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO().readByDelegate(trans, delegate);
4032         try {
4033             if (dbDelgs.isOKhasData()) {
4034                 return mapper.delegate(dbDelgs.value);
4035             } else {
4036                 return Result.err(Status.ERR_DelegateNotFound,"Delegate [%s] is not delegating for anyone.",delegate);
4037             }
4038         } finally {
4039             tt.done();
4040         }        
4041     }
4042
4043 /***********************************
4044  * APPROVAL 
4045  ***********************************/
4046     private static final String APPR_FMT = "actor=%s, action=%s, operation=\"%s\", requestor=%s, delegator=%s";
4047     @Override
4048     public Result<Void> updateApproval(AuthzTrans trans, APPROVALS approvals) {
4049         Result<List<ApprovalDAO.Data>> rlad = mapper.approvals(approvals);
4050         if (rlad.notOK()) {
4051             return Result.err(rlad);
4052         }
4053         int numApprs = rlad.value.size();
4054         if (numApprs<1) {
4055             return Result.err(Status.ERR_NoApprovals,"No Approvals sent for Updating");
4056         }
4057         int numProcessed = 0;
4058         String user = trans.user();
4059         
4060         Result<List<ApprovalDAO.Data>> curr;
4061         Lookup<List<ApprovalDAO.Data>> apprByTicket=null;
4062         for (ApprovalDAO.Data updt : rlad.value) {
4063             if (updt.ticket!=null) {
4064                 curr = ques.approvalDAO().readByTicket(trans, updt.ticket);
4065                 if (curr.isOKhasData()) {
4066                     final List<ApprovalDAO.Data> add = curr.value;
4067                     // Store a Pre-Lookup
4068                     apprByTicket = (trans1, noop) -> add;
4069                 }
4070             } else if (updt.id!=null) {
4071                 curr = ques.approvalDAO().read(trans, updt);
4072             } else if (updt.approver!=null) {
4073                 curr = ques.approvalDAO().readByApprover(trans, updt.approver);
4074             } else {
4075                 return Result.err(Status.ERR_BadData,"Approvals need ID, Ticket or Approval data to update");
4076             }
4077
4078             if (curr.isOKhasData()) {
4079                 Map<String, Result<List<DelegateDAO.Data>>> delegateCache = new HashMap<>();
4080                 Map<UUID, FutureDAO.Data> futureCache = new HashMap<>();
4081                 FutureDAO.Data hasDeleted = new FutureDAO.Data();
4082                 
4083                 for (ApprovalDAO.Data cd : curr.value) {
4084                     if ("pending".equals(cd.status)) {
4085                         // Check for right record.  Need ID, or (Ticket&Trans.User==Appr)
4086                         // If Default ID
4087                         boolean delegatedAction = ques.isDelegated(trans, user, cd.approver, delegateCache);
4088                         String delegator = cd.approver;
4089                         if (updt.id!=null || 
4090                             (updt.ticket!=null && user.equals(cd.approver)) ||
4091                             (updt.ticket!=null && delegatedAction)) {
4092                             if (updt.ticket.equals(cd.ticket)) {
4093                                 Changed ch = new Changed();
4094                                 cd.id = ch.changed(cd.id,updt.id);
4095 //                                cd.ticket = changed(cd.ticket,updt.ticket);
4096                                 cd.user = ch.changed(cd.user,updt.user);
4097                                 cd.approver = ch.changed(cd.approver,updt.approver);
4098                                 cd.type = ch.changed(cd.type,updt.type);
4099                                 cd.status = ch.changed(cd.status,updt.status);
4100                                 cd.memo = ch.changed(cd.memo,updt.memo);
4101                                 cd.operation = ch.changed(cd.operation,updt.operation);
4102                                 cd.updated = ch.changed(cd.updated,updt.updated==null?new Date():updt.updated);
4103 //                                if (updt.status.equals("denied")) {
4104 //                                    cd.last_notified = null;
4105 //                                }
4106                                 if (cd.ticket!=null) {
4107                                     FutureDAO.Data fdd = futureCache.get(cd.ticket);
4108                                     if (fdd==null) { // haven't processed ticket yet
4109                                         Result<FutureDAO.Data> rfdd = ques.futureDAO().readPrimKey(trans, cd.ticket);
4110                                         if (rfdd.isOK()) {
4111                                             fdd = rfdd.value; // null is ok
4112                                         } else {
4113                                             fdd = hasDeleted;
4114                                         }
4115                                         futureCache.put(cd.ticket, fdd); // processed this Ticket... don't do others on this ticket
4116                                     }
4117                                     if (fdd==hasDeleted) { // YES, by Object
4118                                         cd.ticket = null;
4119                                         cd.status = "ticketDeleted";
4120                                         ch.hasChanged(true);
4121                                     } else {
4122                                         FUTURE_OP fop = FUTURE_OP.toFO(cd.operation);
4123                                         if (fop==null) {
4124                                             trans.info().printf("Approval Status %s is not actionable",cd.status);
4125                                         } else if (apprByTicket!=null) {
4126                                             Result<OP_STATUS> rv = func.performFutureOp(trans, fop, fdd, apprByTicket,func.urDBLookup);
4127                                             if (rv.isOK()) {
4128                                                 switch(rv.value) {
4129                                                     case E:
4130                                                         if (delegatedAction) {
4131                                                             trans.audit().printf(APPR_FMT,user,updt.status,cd.memo,cd.user,delegator);
4132                                                         }
4133                                                         futureCache.put(cd.ticket, hasDeleted);
4134                                                         break;
4135                                                     case D:
4136                                                     case L:
4137                                                         ch.hasChanged(true);
4138                                                         trans.audit().printf(APPR_FMT,user,rv.value.desc(),cd.memo,cd.user,delegator);
4139                                                         futureCache.put(cd.ticket, hasDeleted);
4140                                                         break;
4141                                                     default:
4142                                                 }
4143                                             } else {
4144                                                 trans.info().log(rv.toString());
4145                                             }
4146                                         }
4147
4148                                     }
4149                                     ++numProcessed;
4150                                 }
4151                                 if (ch.hasChanged()) {
4152                                     ques.approvalDAO().update(trans, cd, true);
4153                                 }
4154                             }
4155                         }
4156                     }
4157                 }
4158             }
4159         }
4160
4161         if (numApprs==numProcessed) {
4162             return Result.ok();
4163         }
4164         return Result.err(Status.ERR_ActionNotCompleted,numProcessed + " out of " + numApprs + " completed");
4165
4166     }
4167     
4168     private static class Changed {
4169         private boolean hasChanged = false;
4170
4171         public<T> T changed(T src, T proposed) {
4172             if (proposed==null || (src!=null && src.equals(proposed))) {
4173                 return src;
4174             }
4175             hasChanged=true;
4176             return proposed;
4177         }
4178
4179         public void hasChanged(boolean b) {
4180             hasChanged=b;
4181         }
4182
4183         public boolean hasChanged() {
4184             return hasChanged;
4185         }
4186     }
4187
4188     @Override
4189     public Result<APPROVALS> getApprovalsByUser(AuthzTrans trans, String user) {
4190         final Validator v = new ServiceValidator();
4191         if (v.nullOrBlank("User", user).err()) { 
4192             return Result.err(Status.ERR_BadData,v.errs());
4193         }
4194
4195         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByUser(trans, user);
4196         if (rapd.isOK()) {
4197             return mapper.approvals(rapd.value);
4198         } else {
4199             return Result.err(rapd);
4200         }
4201 }
4202
4203     @Override
4204     public Result<APPROVALS> getApprovalsByTicket(AuthzTrans trans, String ticket) {
4205         final Validator v = new ServiceValidator();
4206         if (v.nullOrBlank("Ticket", ticket).err()) { 
4207             return Result.err(Status.ERR_BadData,v.errs());
4208         }
4209         UUID uuid;
4210         try {
4211             uuid = UUID.fromString(ticket);
4212         } catch (IllegalArgumentException e) {
4213             return Result.err(Status.ERR_BadData,e.getMessage());
4214         }
4215     
4216         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByTicket(trans, uuid);
4217         if (rapd.isOK()) {
4218             return mapper.approvals(rapd.value);
4219         } else {
4220             return Result.err(rapd);
4221         }
4222     }
4223     
4224     @Override
4225     public Result<APPROVALS> getApprovalsByApprover(AuthzTrans trans, String approver) {
4226         final Validator v = new ServiceValidator();
4227         if (v.nullOrBlank("Approver", approver).err()) {
4228             return Result.err(Status.ERR_BadData,v.errs());
4229         }
4230         
4231         List<ApprovalDAO.Data> listRapds = new ArrayList<>();
4232         
4233         Result<List<ApprovalDAO.Data>> myRapd = ques.approvalDAO().readByApprover(trans, approver);
4234         if (myRapd.notOK()) {
4235             return Result.err(myRapd);
4236         }
4237         
4238         listRapds.addAll(myRapd.value);
4239         
4240         Result<List<DelegateDAO.Data>> delegatedFor = ques.delegateDAO().readByDelegate(trans, approver);
4241         if (delegatedFor.isOK()) {
4242             for (DelegateDAO.Data dd : delegatedFor.value) {
4243                 if (dd.expires.after(new Date())) {
4244                     String delegator = dd.user;
4245                     Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByApprover(trans, delegator);
4246                     if (rapd.isOK()) {
4247                         for (ApprovalDAO.Data d : rapd.value) { 
4248                             if (!d.user.equals(trans.user())) {
4249                                 listRapds.add(d);
4250                             }
4251                         }
4252                     }
4253                 }
4254             }
4255         }
4256         
4257         return mapper.approvals(listRapds);
4258     }
4259     
4260     /* (non-Javadoc)
4261      * @see org.onap.aaf.auth.service.AuthzService#clearCache(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
4262      */
4263     @Override
4264     public Result<Void> cacheClear(AuthzTrans trans, String cname) {
4265         if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4266             return ques.clearCache(trans,cname);
4267         }
4268         return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4269                 trans.user(),ROOT_NS,CACHE,cname);
4270     }
4271
4272     /* (non-Javadoc)
4273      * @see org.onap.aaf.auth.service.AuthzService#cacheClear(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.Integer)
4274      */
4275     @Override
4276     public Result<Void> cacheClear(AuthzTrans trans, String cname, int[] segment) {
4277         if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4278             Result<Void> v=null;
4279             for (int i: segment) {
4280                 v=ques.cacheClear(trans,cname,i);
4281             }
4282             if (v!=null) {
4283                 return v;
4284             }
4285         }
4286         return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4287                 trans.user(),ROOT_NS,CACHE,cname);
4288     }
4289
4290     /* (non-Javadoc)
4291      * @see org.onap.aaf.auth.service.AuthzService#dbReset(org.onap.aaf.auth.env.test.AuthzTrans)
4292      */
4293     @Override
4294     public void dbReset(AuthzTrans trans) {
4295         ques.historyDAO().reportPerhapsReset(trans, null);
4296     }
4297
4298 }
4299