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