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