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