Improve Batches
[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, boolean includeExpired) {
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, includeExpired);
492             if (rd.isOK()) {
493                 namespace.owner = rd.value;
494             }
495             rd = func.getAdmins(trans, namespace.name, includeExpired);
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.notes = "Extended";
2735             cd.expires = org.expiration(null, Expiration.ExtendPassword,days).getTime();
2736             cd.tag = found.tag;
2737             
2738             cred = ques.credDAO.create(trans, cd);
2739             if (cred.isOK()) {
2740                 return Result.ok();
2741             }
2742             return Result.err(cred);
2743         } finally {
2744             tt.done();
2745         }
2746     }    
2747
2748     private String[] buildVariables(List<CredDAO.Data> value) {
2749         // ensure credentials are sorted so we can fully automate Cred regression test
2750         Collections.sort(value, (cred1, cred2) -> cred1.expires.compareTo(cred2.expires));
2751         String [] vars = new String[value.size()+1];
2752         vars[0]="Choice";
2753         for (int i = 0; i < value.size(); i++) {
2754         vars[i+1] = value.get(i).id + "    " + value.get(i).type 
2755                 + "    |" + value.get(i).expires;
2756         }
2757         return vars;
2758     }
2759     
2760     private String selectCredFromList(List<CredDAO.Data> value, boolean isDelete) {
2761         StringBuilder errMessage = new StringBuilder();
2762         String userPrompt = isDelete?"Select which cred to delete (set force=true to delete all):":"Select which cred to update:";
2763         int numSpaces = value.get(0).id.length() - "Id".length();
2764         
2765         errMessage.append(userPrompt + '\n');
2766         errMessage.append("       Id");
2767         for (int i = 0; i < numSpaces; i++) {
2768             errMessage.append(' ');
2769         }
2770         errMessage.append("   Type  Expires" + '\n');
2771         for (int i=0;i<value.size();++i) {
2772             errMessage.append("    %s\n");
2773         }
2774         errMessage.append("Run same command again with chosen entry as last parameter");
2775         
2776         return errMessage.toString();
2777         
2778     }
2779
2780     @ApiDoc( 
2781             method = DELETE,  
2782             path = "/authn/cred",
2783             params = {},
2784             expectedCode = 200,
2785             errorCodes = {300,403,404,406}, 
2786             text = { "Delete a Credential. If multiple credentials exist for this",
2787                     "ID, you will need to specify which entry you are deleting in the",
2788                     "CredRequest object."
2789                      }
2790             )
2791     @Override
2792     public Result<Void> deleteUserCred(AuthzTrans trans, REQUEST from)  {
2793         final Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2794         final Validator v = new ServiceValidator();
2795         if (v.nullOrBlank("cred", cred.value.id).err()) {
2796             return Result.err(Status.ERR_BadData,v.errs());
2797         }
2798     
2799         Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, cred.value.id);
2800         if (rlcd.notOKorIsEmpty()) {
2801             // Empty Creds should have no user_roles.
2802             Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, cred.value.id);
2803             if (rlurd.isOK()) {
2804                 for (UserRoleDAO.Data data : rlurd.value) {
2805                     ques.userRoleDAO.delete(trans, data, false);
2806                 }
2807             }
2808             return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2809         }
2810         boolean isLastCred = rlcd.value.size()==1;
2811         
2812         MayChange mc = new MayChangeCred(trans,cred.value);
2813         Result<?> rmc = mc.mayChange(); 
2814         if (rmc.notOK()) {
2815             return Result.err(rmc);
2816         }
2817         
2818         int entry = 0;
2819         if (!trans.requested(force)) {
2820             if (rlcd.value.size() > 1) {
2821                 CredRequest cr = (CredRequest)from;
2822                 String inputOption = cr.getEntry();
2823                 if (inputOption == null) {
2824                     String message = selectCredFromList(rlcd.value, true);
2825                     Object[] variables = buildVariables(rlcd.value);
2826                     return Result.err(Status.ERR_ChoiceNeeded, message, variables);
2827                 } else {
2828                     try {
2829                         if (inputOption.length()>5) { // should be a date
2830                             Date d = Chrono.xmlDatatypeFactory.newXMLGregorianCalendar(inputOption).toGregorianCalendar().getTime();
2831                             entry = 0;
2832                             for (CredDAO.Data cd : rlcd.value) {
2833                                 if (cd.type.equals(cr.getType()) && cd.expires.equals(d)) {
2834                                     break;
2835                                 }
2836                                 ++entry;
2837                             }
2838                         } else {
2839                             entry = Integer.parseInt(inputOption) - 1;
2840                         }
2841                     } catch (NullPointerException e) {
2842                         return Result.err(Status.ERR_BadData, "Invalid Date Format for Entry");
2843                     } catch (NumberFormatException e) {
2844                         return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2845                     }
2846                 }
2847                 isLastCred = (entry==-1)?true:false;
2848             } else {
2849                 isLastCred = true;
2850             }
2851             if (entry < -1 || entry >= rlcd.value.size()) {
2852                 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2853             }
2854         }
2855         
2856         Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from,cred.value,false,
2857             () -> "Delete Credential [" +
2858                 cred.value.id +
2859                 ']',
2860             mc);
2861     
2862         Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, cred.value.ns);
2863         if (nsr.notOKorIsEmpty()) {
2864             return Result.err(nsr);
2865         }
2866     
2867         switch(fd.status) {
2868             case OK:
2869                 Result<String> rfc = func.createFuture(trans, fd.value, cred.value.id,
2870                         trans.user(), nsr.value.get(0), FUTURE_OP.D);
2871     
2872                 if (rfc.isOK()) {
2873                     return Result.err(Status.ACC_Future, "Credential Delete [%s] is saved for future processing",cred.value.id);
2874                 } else { 
2875                     return Result.err(rfc);
2876                 }
2877             case Status.ACC_Now:
2878                 Result<?>udr = null;
2879                 if (!trans.requested(force)) {
2880                     if (entry<0 || entry >= rlcd.value.size()) {
2881                         return Result.err(Status.ERR_BadData,"Invalid Choice [" + entry + "] chosen for Delete [%s] is saved for future processing",cred.value.id);
2882                     }
2883                     udr = ques.credDAO.delete(trans, rlcd.value.get(entry),false);
2884                 } else {
2885                     for (CredDAO.Data curr : rlcd.value) {
2886                         udr = ques.credDAO.delete(trans, curr, false);
2887                         if (udr.notOK()) {
2888                             return Result.err(udr);
2889                         }
2890                     }
2891                 }
2892                 if (isLastCred) {
2893                     Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, cred.value.id);
2894                     if (rlurd.isOK()) {
2895                         for (UserRoleDAO.Data data : rlurd.value) {
2896                             ques.userRoleDAO.delete(trans, data, false);
2897                         }
2898                     }
2899                 }
2900                 if (udr==null) {
2901                     return Result.err(Result.ERR_NotFound,"No User Data found");
2902                 }
2903                 if (udr.isOK()) {
2904                     return Result.ok();
2905                 }
2906                 return Result.err(udr);
2907             default:
2908                 return Result.err(fd);
2909         }
2910     
2911     }
2912
2913
2914     @Override
2915     public Result<Date> doesCredentialMatch(AuthzTrans trans, REQUEST credReq) {
2916         TimeTaken tt = trans.start("Does Credential Match", Env.SUB);
2917         try {
2918             // Note: Mapper assigns RAW type
2919             Result<CredDAO.Data> data = mapper.cred(trans, credReq,false);
2920             if (data.notOKorIsEmpty()) {
2921                 return Result.err(data);
2922             }
2923             CredDAO.Data cred = data.value;    // of the Mapped Cred
2924             if (cred.cred==null) {
2925                 return Result.err(Result.ERR_BadData,"No Password");
2926             } else {
2927                 return ques.doesUserCredMatch(trans, cred.id, cred.cred.array());
2928             }
2929
2930         } catch (DAOException e) {
2931             trans.error().log(e,"Error looking up cred");
2932             return Result.err(Status.ERR_Denied,"Credential does not match");
2933         } finally {
2934             tt.done();
2935         }
2936     }
2937
2938     @ApiDoc( 
2939             method = GET,  
2940             path = "/authn/basicAuth",
2941             params = {},
2942             expectedCode = 200,
2943             errorCodes = { 403 }, 
2944             text = { "!!!! DEPRECATED without X509 Authentication STOP USING THIS API BY DECEMBER 2017, or use Certificates !!!!\n" 
2945                     + "Use /authn/validate instead\n"
2946                     + "Note: Validate a Password using BasicAuth Base64 encoded Header. This HTTP/S call is intended as a fast"
2947                     + " User/Password lookup for Security Frameworks, and responds 200 if it passes BasicAuth "
2948                 + "security, and 403 if it does not." }
2949             )
2950     private void basicAuth() {
2951         // This is a place holder for Documentation.  The real BasicAuth API does not call Service.
2952     }
2953     
2954     @ApiDoc( 
2955             method = POST,  
2956             path = "/authn/validate",
2957             params = {},
2958             expectedCode = 200,
2959             errorCodes = { 403 }, 
2960             text = { "Validate a Credential given a Credential Structure.  This is a more comprehensive validation, can "
2961                     + "do more than BasicAuth as Credential types exp" }
2962             )
2963     @Override
2964     public Result<Date> validateBasicAuth(AuthzTrans trans, String basicAuth) {
2965         //TODO how to make sure people don't use this in browsers?  Do we care?
2966         TimeTaken tt = trans.start("Validate Basic Auth", Env.SUB);
2967         try {
2968             BasicPrincipal bp = new BasicPrincipal(basicAuth,trans.org().getRealm());
2969             Result<Date> rq = ques.doesUserCredMatch(trans, bp.getName(), bp.getCred());
2970             // Note: Only want to log problem, don't want to send back to end user
2971             if (rq.isOK()) {
2972                 return rq;
2973             } else {
2974                 trans.audit().log(rq.errorString());
2975             }
2976         } catch (Exception e) {
2977             trans.warn().log(e);
2978         } finally {
2979             tt.done();
2980         }
2981         return Result.err(Status.ERR_Denied,"Bad Basic Auth");
2982     }
2983
2984 /***********************************
2985  * USER-ROLE 
2986  ***********************************/
2987     @ApiDoc( 
2988             method = POST,  
2989             path = "/authz/userRole",
2990             params = {},
2991             expectedCode = 201,
2992             errorCodes = {403,404,406,409}, 
2993             text = { "Create a UserRole relationship (add User to Role)",
2994                      "A UserRole is an object Representation of membership of a Role for limited time.",
2995                      "If a shorter amount of time for Role ownership is required, use the 'End' field.",
2996                      "** Note: Owners of Namespaces will be required to revalidate users in these roles ",
2997                      "before Expirations expire.  Namespace owners will be notified by email."
2998                    }
2999             )
3000     @Override
3001     public Result<Void> createUserRole(final AuthzTrans trans, REQUEST from) {
3002         TimeTaken tt = trans.start("Create UserRole", Env.SUB);
3003         try {
3004             Result<UserRoleDAO.Data> urr = mapper.userRole(trans, from);
3005             if (urr.notOKorIsEmpty()) {
3006                 return Result.err(urr);
3007             }
3008             final UserRoleDAO.Data userRole = urr.value;
3009             
3010             final ServiceValidator v = new ServiceValidator();
3011             if (v.user_role(userRole).err() ||
3012                v.user(trans.org(), userRole.user).err()) {
3013                 return Result.err(Status.ERR_BadData,v.errs());
3014             }
3015
3016
3017              
3018             // Check if user can change first
3019             Result<FutureDAO.Data> fd = mapper.future(trans,UserRoleDAO.TABLE,from,urr.value,true, // may request Approvals
3020                 () -> "Add User [" + userRole.user + "] to Role [" +
3021                         userRole.role +
3022                         ']',
3023                 new MayChange() {
3024                     private Result<NsDAO.Data> nsd;
3025                     @Override
3026                     public Result<?> mayChange() {
3027                         if (nsd==null) {
3028                             RoleDAO.Data r = RoleDAO.Data.decode(userRole);
3029                             nsd = ques.mayUser(trans, trans.user(), r, Access.write);
3030                         }
3031                         return nsd;
3032                     }
3033                 });
3034             Result<NsDAO.Data> nsr = ques.deriveNs(trans, userRole.role);
3035             if (nsr.notOKorIsEmpty()) {
3036                 return Result.err(nsr);
3037             }
3038
3039             switch(fd.status) {
3040                 case OK:
3041                     Result<String> rfc = func.createFuture(trans, fd.value, userRole.user+'|'+userRole.ns + '.' + userRole.rname, 
3042                             userRole.user, nsr.value, FUTURE_OP.C);
3043                     if (rfc.isOK()) {
3044                         return Result.err(Status.ACC_Future, "UserRole [%s - %s.%s] is saved for future processing",
3045                                 userRole.user,
3046                                 userRole.ns,
3047                                 userRole.rname);
3048                     } else { 
3049                         return Result.err(rfc);
3050                     }
3051                 case Status.ACC_Now:
3052                     return func.addUserRole(trans, userRole);
3053                 default:
3054                     return Result.err(fd);
3055             }
3056         } finally {
3057             tt.done();
3058         }
3059     }
3060     
3061         /**
3062          * getUserRolesByRole
3063          */
3064         @ApiDoc(
3065                 method = GET,
3066                 path = "/authz/userRoles/role/:role",
3067                 params = {"role|string|true"},
3068                 expectedCode = 200,
3069                 errorCodes = {404,406},
3070                 text = { "List all Users that are attached to Role specified in :role",
3071                         }
3072                )
3073         @Override
3074         public Result<USERROLES> getUserRolesByRole(AuthzTrans trans, String role) {
3075             final Validator v = new ServiceValidator();
3076             if (v.nullOrBlank("Role",role).err()) {
3077                 return Result.err(Status.ERR_BadData,v.errs());
3078             }
3079             
3080             Result<RoleDAO.Data> rrdd;
3081             rrdd = RoleDAO.Data.decode(trans,ques,role);
3082             if (rrdd.notOK()) {
3083                 return Result.err(rrdd);
3084             }
3085             // May Requester see result?
3086             Result<NsDAO.Data> ns = ques.mayUser(trans,trans.user(), rrdd.value,Access.read);
3087             if (ns.notOK()) {
3088                 return Result.err(ns);
3089             }
3090     
3091     //        boolean filter = true;        
3092     //        if (ns.value.isAdmin(trans.user()) || ns.value.isResponsible(trans.user()))
3093     //            filter = false;
3094             
3095             // Get list of roles per user, then add to Roles as we go
3096             HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3097             Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3098             if (rlurd.isOK()) {
3099                 for (UserRoleDAO.Data data : rlurd.value) {
3100                     userSet.add(data);
3101                 }
3102             }
3103             
3104             @SuppressWarnings("unchecked")
3105             USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3106             // Checked for permission
3107             mapper.userRoles(trans, userSet, users);
3108             return Result.ok(users);
3109         }
3110         /**
3111          * getUserRolesByRole
3112          */
3113         @ApiDoc(
3114                 method = GET,
3115                 path = "/authz/userRoles/user/:user",
3116                 params = {"role|string|true"},
3117                 expectedCode = 200,
3118                 errorCodes = {404,406},
3119                 text = { "List all UserRoles for :user",
3120                         }
3121                )
3122         @Override
3123         public Result<USERROLES> getUserRolesByUser(AuthzTrans trans, String user) {
3124             final Validator v = new ServiceValidator();
3125             if (v.nullOrBlank("User",user).err()) {
3126                 return Result.err(Status.ERR_BadData,v.errs());
3127             }
3128             
3129             // Get list of roles per user, then add to Roles as we go
3130             Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, user);
3131             if (rlurd.notOK()) { 
3132                 return Result.err(rlurd);
3133             }
3134             
3135             /* Check for
3136              *   1) is User 
3137              *   2) is User's Supervisor
3138              *   3) Has special global access =read permission
3139              *   
3140              *   If none of the 3, then filter results to NSs in which Calling User has Ns.access * read
3141              */
3142             boolean mustFilter;
3143             String callingUser = trans.getUserPrincipal().getName();
3144             NsDAO.Data ndd = new NsDAO.Data();
3145
3146             if (user.equals(callingUser)) {
3147                 mustFilter = false;
3148             } else {
3149                 Organization org = trans.org();
3150                 try {
3151                     Identity orgID = org.getIdentity(trans, user);
3152                     Identity manager = orgID==null?null:orgID.responsibleTo();
3153                     if (orgID!=null && (manager!=null && callingUser.equals(manager.fullID()))) {
3154                         mustFilter = false;
3155                     } else if (ques.isGranted(trans, callingUser, ROOT_NS, Question.ACCESS, "*", Access.read.name())) {
3156                         mustFilter=false;
3157                     } else {
3158                         mustFilter = true;
3159                     }
3160                 } catch (OrganizationException e) {
3161                     trans.env().log(e);
3162                     mustFilter = true;
3163                 }
3164             }
3165             
3166             List<UserRoleDAO.Data> content;
3167             if (mustFilter) {
3168                 content = new ArrayList<>(rlurd.value.size()); // avoid multi-memory redos
3169                 
3170                 for (UserRoleDAO.Data data : rlurd.value) {
3171                     ndd.name=data.ns;
3172                     Result<Data> mur = ques.mayUser(trans, callingUser, ndd, Access.read);
3173                     if (mur.isOK()){
3174                         content.add(data);
3175                     }
3176                 }
3177                 
3178             } else {
3179                 content = rlurd.value;
3180             }
3181
3182
3183             @SuppressWarnings("unchecked")
3184             USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3185             // Checked for permission
3186             mapper.userRoles(trans, content, users);
3187             return Result.ok(users);
3188         }
3189
3190         
3191     @ApiDoc( 
3192             method = PUT,  
3193             path = "/authz/userRole/user",
3194             params = {},
3195             expectedCode = 200,
3196             errorCodes = {403,404,406}, 
3197             text = { "Set a User's roles to the roles specified in the UserRoleRequest object.",
3198                         "WARNING: Roles supplied will be the ONLY roles attached to this user",
3199                         "If no roles are supplied, user's roles are reset."
3200                    }
3201             )
3202     @Override
3203     public Result<Void> resetRolesForUser(AuthzTrans trans, REQUEST rreq) {
3204         Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3205         final ServiceValidator v = new ServiceValidator();
3206         if (rurdd.notOKorIsEmpty()) {
3207             return Result.err(rurdd);
3208         }
3209         if (v.user(trans.org(), rurdd.value.user).err()) {
3210             return Result.err(Status.ERR_BadData,v.errs());
3211         }
3212
3213         Set<String> currRoles = new HashSet<>();
3214         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, rurdd.value.user);
3215         if (rlurd.isOK()) {
3216             for (UserRoleDAO.Data data : rlurd.value) {
3217                 currRoles.add(data.role);
3218             }
3219         }
3220         
3221         Result<Void> rv = null;
3222         String[] roles;
3223         if (rurdd.value.role==null) {
3224             roles = new String[0];
3225         } else {
3226             roles = rurdd.value.role.split(",");
3227         }
3228         
3229         for (String role : roles) {            
3230             if (v.role(role).err()) {
3231                 return Result.err(Status.ERR_BadData,v.errs());
3232             }
3233             Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3234             if (rrdd.notOK()) {
3235                 return Result.err(rrdd);
3236             }
3237             
3238             rurdd.value.role(rrdd.value);
3239             
3240             Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rrdd.value,Access.write);
3241             if (nsd.notOK()) {
3242                 return Result.err(nsd);
3243             }
3244             Result<NsDAO.Data> nsr = ques.deriveNs(trans, role);
3245             if (nsr.notOKorIsEmpty()) {
3246                 return Result.err(nsr);    
3247             }
3248             
3249             if (currRoles.contains(role)) {
3250                 currRoles.remove(role);
3251             } else {
3252                 rv = func.addUserRole(trans, rurdd.value);
3253                 if (rv.notOK()) {
3254                     return rv;
3255                 }
3256             }
3257         }
3258         
3259         for (String role : currRoles) {
3260             rurdd.value.role(trans,ques,role);
3261             rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3262             if (rv.notOK()) {
3263                 trans.info().log(rurdd.value.user,"/",rurdd.value.role, "expected to be deleted, but does not exist");
3264                 // return rv; // if it doesn't exist, don't error out
3265             }
3266
3267         }
3268     
3269         return Result.ok();        
3270         
3271     }
3272     
3273     @ApiDoc( 
3274             method = PUT,  
3275             path = "/authz/userRole/role",
3276             params = {},
3277             expectedCode = 200,
3278             errorCodes = {403,404,406}, 
3279             text = { "Set a Role's users to the users specified in the UserRoleRequest object.",
3280                     "WARNING: Users supplied will be the ONLY users attached to this role",
3281                     "If no users are supplied, role's users are reset."
3282                }
3283             )
3284     @Override
3285     public Result<Void> resetUsersForRole(AuthzTrans trans, REQUEST rreq) {
3286         Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3287         if (rurdd.notOKorIsEmpty()) {
3288             return Result.err(rurdd);
3289         }
3290         final ServiceValidator v = new ServiceValidator();
3291         if (v.user_role(rurdd.value).err()) {
3292             return Result.err(Status.ERR_BadData,v.errs());
3293         }
3294
3295         RoleDAO.Data rd = RoleDAO.Data.decode(rurdd.value);
3296
3297         Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rd, Access.write);
3298         if (nsd.notOK()) {
3299             return Result.err(nsd);
3300         }
3301
3302         Result<NsDAO.Data> nsr = ques.deriveNs(trans, rurdd.value.role);
3303         if (nsr.notOKorIsEmpty()) {
3304             return Result.err(nsr);    
3305         }
3306
3307         Set<String> currUsers = new HashSet<>();
3308         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, rurdd.value.role);
3309         if (rlurd.isOK()) { 
3310             for (UserRoleDAO.Data data : rlurd.value) {
3311                 currUsers.add(data.user);
3312             }
3313         }
3314     
3315         // found when connected remotely to DEVL, can't replicate locally
3316         // inconsistent errors with cmd: role user setTo [nothing]
3317         // deleteUserRole --> read --> get --> cacheIdx(?)
3318         // sometimes returns idx for last added user instead of user passed in
3319         // cache bug? 
3320         
3321         
3322         Result<Void> rv = null;
3323         String[] users = {};
3324         if (rurdd.value.user != null) {
3325             users = rurdd.value.user.split(",");
3326         }
3327         
3328         for (String user : users) {            
3329             if (v.user(trans.org(), user).err()) {
3330                 return Result.err(Status.ERR_BadData,v.errs());
3331             }
3332             rurdd.value.user = user;
3333
3334             if (currUsers.contains(user)) {
3335                 currUsers.remove(user);
3336             } else {
3337                 rv = func.addUserRole(trans, rurdd.value);
3338                 if (rv.notOK()) { 
3339                     return rv;
3340                 }
3341             }
3342         }
3343         
3344         for (String user : currUsers) {
3345             rurdd.value.user = user; 
3346             rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3347             if (rv.notOK()) {
3348                 trans.info().log(rurdd.value, "expected to be deleted, but not exists");
3349                 return rv;
3350             }
3351         }    
3352         
3353         return Result.ok();            
3354     }
3355     
3356     @ApiDoc(
3357             method = GET,
3358             path = "/authz/userRole/extend/:user/:role",
3359             params = {    "user|string|true",
3360                         "role|string|true"
3361                     },
3362             expectedCode = 200,
3363             errorCodes = {403,404,406},
3364             text = { "Extend the Expiration of this User Role by the amount set by Organization",
3365                      "Requestor must be allowed to modify the role"
3366                     }
3367            )
3368     @Override
3369     public Result<Void> extendUserRole(AuthzTrans trans, String user, String role) {
3370         Organization org = trans.org();
3371         final ServiceValidator v = new ServiceValidator();
3372         if (v.user(org, user)
3373             .role(role)
3374             .err()) {
3375             return Result.err(Status.ERR_BadData,v.errs());
3376         }
3377     
3378         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3379         if (rrdd.notOK()) {
3380             return Result.err(rrdd);
3381         }
3382         
3383         Result<NsDAO.Data> rcr = ques.mayUser(trans, trans.user(), rrdd.value, Access.write);
3384         boolean mayNotChange;
3385         if ((mayNotChange = rcr.notOK()) && !trans.requested(future)) {
3386             return Result.err(rcr);
3387         }
3388         
3389         Result<List<UserRoleDAO.Data>> rr = ques.userRoleDAO.read(trans, user,role);
3390         if (rr.notOK()) {
3391             return Result.err(rr);
3392         }
3393         for (UserRoleDAO.Data userRole : rr.value) {
3394             if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3395                 FutureDAO.Data fto = new FutureDAO.Data();
3396                 fto.target=UserRoleDAO.TABLE;
3397                 fto.memo = "Extend User ["+userRole.user+"] in Role ["+userRole.role+"]";
3398                 GregorianCalendar now = new GregorianCalendar();
3399                 fto.start = now.getTime();
3400                 fto.expires = org.expiration(now, Expiration.Future).getTime();
3401                 try {
3402                     fto.construct = userRole.bytify();
3403                 } catch (IOException e) {
3404                     trans.error().log(e, "Error while bytifying UserRole for Future");
3405                     return Result.err(e);
3406                 }
3407
3408                 Result<String> rfc = func.createFuture(trans, fto, 
3409                         userRole.user+'|'+userRole.role, userRole.user, rcr.value, FUTURE_OP.U);
3410                 if (rfc.isOK()) {
3411                     return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3412                             userRole.user,
3413                             userRole.role);
3414                 } else {
3415                     return Result.err(rfc);
3416                 }
3417             } else {
3418                 return func.extendUserRole(trans, userRole, false);
3419             }
3420         }
3421         return Result.err(Result.ERR_NotFound,"This user and role doesn't exist");
3422     }
3423
3424     @ApiDoc( 
3425             method = DELETE,  
3426             path = "/authz/userRole/:user/:role",
3427             params = {    "user|string|true",
3428                         "role|string|true"
3429                     },
3430             expectedCode = 200,
3431             errorCodes = {403,404,406}, 
3432             text = { "Remove Role :role from User :user."
3433                    }
3434             )
3435     @Override
3436     public Result<Void> deleteUserRole(AuthzTrans trans, String usr, String role) {
3437         Validator val = new ServiceValidator();
3438         if (val.nullOrBlank("User", usr)
3439               .nullOrBlank("Role", role).err()) {
3440             return Result.err(Status.ERR_BadData, val.errs());
3441         }
3442
3443         boolean mayNotChange;
3444         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3445         if (rrdd.notOK()) {
3446             return Result.err(rrdd);
3447         }
3448         
3449         RoleDAO.Data rdd = rrdd.value;
3450         Result<NsDAO.Data> rns = ques.mayUser(trans, trans.user(), rdd, Access.write);
3451
3452         // Make sure we don't delete the last owner of valid NS
3453         if (rns.isOKhasData() && Question.OWNER.equals(rdd.name) && ques.countOwner(trans,rdd.ns)<=1) {
3454             return Result.err(Status.ERR_Denied,"You may not delete the last Owner of " + rdd.ns );
3455         }
3456         
3457         if (mayNotChange=rns.notOK()) {
3458             if (!trans.requested(future)) {
3459                 return Result.err(rns);
3460             }
3461         }
3462
3463         Result<List<UserRoleDAO.Data>> rulr;
3464         if ((rulr=ques.userRoleDAO.read(trans, usr, role)).notOKorIsEmpty()) {
3465             return Result.err(Status.ERR_UserRoleNotFound, "User [ "+usr+" ] is not "
3466                     + "Assigned to the Role [ " + role + " ]");
3467         }
3468
3469         UserRoleDAO.Data userRole = rulr.value.get(0);
3470         if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3471             FutureDAO.Data fto = new FutureDAO.Data();
3472             fto.target=UserRoleDAO.TABLE;
3473             fto.memo = "Remove User ["+userRole.user+"] from Role ["+userRole.role+"]";
3474             GregorianCalendar now = new GregorianCalendar();
3475             fto.start = now.getTime();
3476             fto.expires = trans.org().expiration(now, Expiration.Future).getTime();
3477
3478             Result<String> rfc = func.createFuture(trans, fto, 
3479                     userRole.user+'|'+userRole.role, userRole.user, rns.value, FUTURE_OP.D);
3480             if (rfc.isOK()) {
3481                 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing", 
3482                         userRole.user,
3483                         userRole.role);
3484             } else { 
3485                 return Result.err(rfc);
3486             }
3487         } else {
3488             return ques.userRoleDAO.delete(trans, rulr.value.get(0), false);
3489         }
3490     }
3491
3492     @ApiDoc( 
3493             method = GET,  
3494             path = "/authz/userRole/:user/:role",
3495             params = {"user|string|true",
3496                       "role|string|true"},
3497             expectedCode = 200,
3498             errorCodes = {403,404,406}, 
3499             text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3500                    }
3501             )
3502     @Override
3503     public Result<USERS> getUserInRole(AuthzTrans trans, String user, String role) {
3504         final Validator v = new ServiceValidator();
3505         if (v.role(role).nullOrBlank("User", user).err()) {
3506             return Result.err(Status.ERR_BadData,v.errs());
3507         }
3508
3509 //        Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3510 //        if (ns.notOK()) return Result.err(ns);
3511 //        
3512 //        Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3513         // May calling user see by virtue of the Role
3514         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3515         if (rrdd.notOK()) {
3516             return Result.err(rrdd);
3517         }
3518         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3519         if (rnd.notOK()) {
3520             return Result.err(rnd); 
3521         }
3522         
3523         HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3524         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readUserInRole(trans, user, role);
3525         if (rlurd.isOK()) {
3526             for (UserRoleDAO.Data data : rlurd.value) {
3527                 userSet.add(data);
3528             }
3529         }
3530         
3531         @SuppressWarnings("unchecked")
3532         USERS users = (USERS) mapper.newInstance(API.USERS);
3533         mapper.users(trans, userSet, users);
3534         return Result.ok(users);
3535     }
3536
3537     @ApiDoc( 
3538             method = GET,  
3539             path = "/authz/users/role/:role",
3540             params = {"user|string|true",
3541                       "role|string|true"},
3542             expectedCode = 200,
3543             errorCodes = {403,404,406}, 
3544             text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3545                    }
3546             )
3547     @Override
3548     public Result<USERS> getUsersByRole(AuthzTrans trans, String role) {
3549         final Validator v = new ServiceValidator();
3550         if (v.nullOrBlank("Role",role).err()) {
3551             return Result.err(Status.ERR_BadData,v.errs());
3552         }
3553
3554 //        Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3555 //        if (ns.notOK()) return Result.err(ns);
3556 //        
3557 //        Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3558         // May calling user see by virtue of the Role
3559         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3560         if (rrdd.notOK()) {
3561             return Result.err(rrdd);
3562         }
3563         
3564         boolean contactOnly = false;
3565         // Allow the request of any valid user to find the contact of the NS (Owner)
3566         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3567         if (rnd.notOK()) {
3568             if (Question.OWNER.equals(rrdd.value.name)) {
3569                 contactOnly = true;
3570             } else {
3571                 return Result.err(rnd);
3572             }
3573         }
3574         
3575         HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3576         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3577         if (rlurd.isOK()) { 
3578             for (UserRoleDAO.Data data : rlurd.value) {
3579                 if (contactOnly) { //scrub data
3580                     // Can't change actual object, or will mess up the cache.
3581                     UserRoleDAO.Data scrub = new UserRoleDAO.Data();
3582                     scrub.ns = data.ns;
3583                     scrub.rname = data.rname;
3584                     scrub.role = data.role;
3585                     scrub.user = data.user;
3586                     userSet.add(scrub);
3587                 } else {
3588                     userSet.add(data);
3589                 }
3590             }
3591         }
3592         
3593         @SuppressWarnings("unchecked")
3594         USERS users = (USERS) mapper.newInstance(API.USERS);
3595         mapper.users(trans, userSet, users);
3596         return Result.ok(users);
3597     }
3598
3599     /**
3600      * getUsersByPermission
3601      */
3602     @ApiDoc(
3603             method = GET,
3604             path = "/authz/users/perm/:type/:instance/:action",
3605             params = {    "type|string|true",
3606                         "instance|string|true",
3607                         "action|string|true"
3608                     },
3609             expectedCode = 200,
3610             errorCodes = {404,406},
3611             text = { "List all Users that have Permission specified by :type :instance :action",
3612                     }
3613            )
3614     @Override
3615     public Result<USERS> getUsersByPermission(AuthzTrans trans, String type, String instance, String action) {
3616         final Validator v = new ServiceValidator();
3617         if (v.nullOrBlank("Type",type)
3618             .nullOrBlank("Instance",instance)
3619             .nullOrBlank("Action",action)            
3620             .err()) {
3621             return Result.err(Status.ERR_BadData,v.errs());
3622         }
3623
3624         Result<NsSplit> nss = ques.deriveNsSplit(trans, type);
3625         if (nss.notOK()) {
3626             return Result.err(nss);
3627         }
3628         
3629         Result<List<NsDAO.Data>> nsd = ques.nsDAO.read(trans, nss.value.ns);
3630         if (nsd.notOK()) {
3631             return Result.err(nsd);
3632         }
3633         
3634         boolean allInstance = ASTERIX.equals(instance);
3635         boolean allAction = ASTERIX.equals(action);
3636         // Get list of roles per Permission, 
3637         // Then loop through Roles to get Users
3638         // Note: Use Sets to avoid processing or responding with Duplicates
3639         Set<String> roleUsed = new HashSet<>();
3640         Set<UserRoleDAO.Data> userSet = new HashSet<>();
3641         
3642         if (!nss.isEmpty()) {
3643             Result<List<PermDAO.Data>> rlp = ques.permDAO.readByType(trans, nss.value.ns, nss.value.name);
3644             if (rlp.isOKhasData()) {
3645                 for (PermDAO.Data pd : rlp.value) {
3646                     if ((allInstance || pd.instance.equals(instance)) && 
3647                             (allAction || pd.action.equals(action))) {
3648                         if (ques.mayUser(trans, trans.user(),pd,Access.read).isOK()) {
3649                             for (String role : pd.roles) {
3650                                 if (!roleUsed.contains(role)) { // avoid evaluating Role many times
3651                                     roleUsed.add(role);
3652                                     Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role.replace('|', '.'));
3653                                     if (rlurd.isOKhasData()) {
3654                                         for (UserRoleDAO.Data urd : rlurd.value) {
3655                                             userSet.add(urd);
3656                                         }
3657                                     }
3658                                 }
3659                             }
3660                         }
3661                     }
3662                 }
3663             }
3664         }
3665         @SuppressWarnings("unchecked")
3666         USERS users = (USERS) mapper.newInstance(API.USERS);
3667         mapper.users(trans, userSet, users);
3668         return Result.ok(users);
3669     }
3670
3671     /***********************************
3672  * HISTORY 
3673  ***********************************/    
3674     @Override
3675     public Result<HISTORY> getHistoryByUser(final AuthzTrans trans, String user, final int[] yyyymm, final int sort) {    
3676         final Validator v = new ServiceValidator();
3677         if (v.nullOrBlank("User",user).err()) {
3678             return Result.err(Status.ERR_BadData,v.errs());
3679         }
3680
3681         Result<NsDAO.Data> rnd;
3682         // Users may look at their own data
3683          if (trans.user().equals(user)) {
3684                 // Users may look at their own data
3685          } else {
3686             int at = user.indexOf('@');
3687             if (at>=0 && trans.org().getRealm().equals(user.substring(at+1))) {
3688                 NsDAO.Data nsd  = new NsDAO.Data();
3689                 nsd.name = Question.domain2ns(user);
3690                 rnd = ques.mayUser(trans, trans.user(), nsd, Access.read);
3691                 if (rnd.notOK()) {
3692                     return Result.err(rnd);
3693                 }
3694             } else {
3695                 rnd = ques.validNSOfDomain(trans, user);
3696                 if (rnd.notOK()) {
3697                     return Result.err(rnd);
3698                 }
3699
3700                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3701                 if (rnd.notOK()) {
3702                     return Result.err(rnd);
3703                 }
3704             }
3705          }
3706         Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readByUser(trans, user, yyyymm);
3707         if (resp.notOK()) {
3708             return Result.err(resp);
3709         }
3710         return mapper.history(trans, resp.value,sort);
3711     }
3712
3713     @Override
3714     public Result<HISTORY> getHistoryByRole(AuthzTrans trans, String role, int[] yyyymm, final int sort) {
3715         final Validator v = new ServiceValidator();
3716         if (v.nullOrBlank("Role",role).err()) {
3717             return Result.err(Status.ERR_BadData,v.errs());
3718         }
3719
3720         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3721         if (rrdd.notOK()) {
3722             return Result.err(rrdd);
3723         }
3724         
3725         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
3726         if (rnd.notOK()) {
3727             return Result.err(rnd);
3728         }
3729         Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, role, "role", yyyymm); 
3730         if (resp.notOK()) {
3731             return Result.err(resp);
3732         }
3733         return mapper.history(trans, resp.value,sort);
3734     }
3735
3736     @Override
3737     public Result<HISTORY> getHistoryByPerm(AuthzTrans trans, String type, int[] yyyymm, final int sort) {
3738         final Validator v = new ServiceValidator();
3739         if (v.nullOrBlank("Type",type)
3740             .err()) {
3741             return Result.err(Status.ERR_BadData,v.errs());
3742         }
3743
3744         // May user see Namespace of Permission (since it's only one piece... we can't check for "is permission part of")
3745         Result<NsDAO.Data> rnd = ques.deriveNs(trans,type);
3746         if (rnd.notOK()) {
3747             return Result.err(rnd);
3748         }
3749         
3750         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3751         if (rnd.notOK()) {
3752             return Result.err(rnd);    
3753         }
3754         Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, type, "perm", yyyymm);
3755         if (resp.notOK()) {
3756             return Result.err(resp);
3757         }
3758         return mapper.history(trans, resp.value,sort);
3759     }
3760
3761     @Override
3762     public Result<HISTORY> getHistoryByNS(AuthzTrans trans, String ns, int[] yyyymm, final int sort) {
3763         final Validator v = new ServiceValidator();
3764         if (v.nullOrBlank("NS",ns)
3765             .err()) { 
3766             return Result.err(Status.ERR_BadData,v.errs());
3767         }
3768
3769         Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
3770         if (rnd.notOK()) {
3771             return Result.err(rnd);
3772         }
3773         rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3774         if (rnd.notOK()) {
3775             return Result.err(rnd);    
3776         }
3777
3778         Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, ns, "ns", yyyymm);
3779         if (resp.notOK()) {
3780             return Result.err(resp);
3781         }
3782         return mapper.history(trans, resp.value,sort);
3783     }
3784
3785 /***********************************
3786  * DELEGATE 
3787  ***********************************/
3788     @Override
3789     public Result<Void> createDelegate(final AuthzTrans trans, REQUEST base) {
3790         return createOrUpdateDelegate(trans, base, Question.Access.create);
3791     }
3792
3793     @Override
3794     public Result<Void> updateDelegate(AuthzTrans trans, REQUEST base) {
3795         return createOrUpdateDelegate(trans, base, Question.Access.write);
3796     }
3797
3798
3799     private Result<Void> createOrUpdateDelegate(final AuthzTrans trans, REQUEST base, final Access access) {
3800         final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3801         final ServiceValidator v = new ServiceValidator();
3802         if (v.delegate(trans.org(),rd).err()) { 
3803             return Result.err(Status.ERR_BadData,v.errs());
3804         }
3805
3806         final DelegateDAO.Data dd = rd.value;
3807         
3808         Result<List<DelegateDAO.Data>> ddr = ques.delegateDAO.read(trans, dd);
3809         if (access==Access.create && ddr.isOKhasData()) {
3810             return Result.err(Status.ERR_ConflictAlreadyExists, "[%s] already delegates to [%s]", dd.user, ddr.value.get(0).delegate);
3811         } else if (access!=Access.create && ddr.notOKorIsEmpty()) { 
3812             return Result.err(Status.ERR_NotFound, "[%s] does not have a Delegate Record to [%s].",dd.user,access.name());
3813         }
3814         Result<Void> rv = ques.mayUser(trans, dd, access);
3815         if (rv.notOK()) {
3816             return rv;
3817         }
3818         
3819         Result<FutureDAO.Data> fd = mapper.future(trans,DelegateDAO.TABLE,base, dd, false,
3820             () -> {
3821                 StringBuilder sb = new StringBuilder();
3822                 sb.append(access.name());
3823                 sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
3824                 sb.append("Delegate ");
3825                 sb.append(access==Access.create?"[":"to [");
3826                 sb.append(rd.value.delegate);
3827                 sb.append("] for [");
3828                 sb.append(rd.value.user);
3829                 sb.append(']');
3830                 return sb.toString();
3831             },
3832             () -> {
3833                 return Result.ok(); // Validate in code above
3834             });
3835         
3836         switch(fd.status) {
3837             case OK:
3838                 Result<String> rfc = func.createFuture(trans, fd.value, 
3839                         dd.user, trans.user(),null, access==Access.create?FUTURE_OP.C:FUTURE_OP.U);
3840                 if (rfc.isOK()) { 
3841                     return Result.err(Status.ACC_Future, "Delegate for [%s]",
3842                             dd.user);
3843                 } else { 
3844                     return Result.err(rfc);
3845                 }
3846             case Status.ACC_Now:
3847                 if (access==Access.create) {
3848                     Result<DelegateDAO.Data> rdr = ques.delegateDAO.create(trans, dd);
3849                     if (rdr.isOK()) {
3850                         return Result.ok();
3851                     } else {
3852                         return Result.err(rdr);
3853                     }
3854                 } else {
3855                     return ques.delegateDAO.update(trans, dd);
3856                 }
3857             default:
3858                 return Result.err(fd);
3859         }
3860     }
3861
3862     @Override
3863     public Result<Void> deleteDelegate(AuthzTrans trans, REQUEST base) {
3864         final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3865         final Validator v = new ServiceValidator();
3866         if (v.notOK(rd).nullOrBlank("User", rd.value.user).err()) {
3867             return Result.err(Status.ERR_BadData,v.errs());
3868         }
3869         
3870         Result<List<DelegateDAO.Data>> ddl;
3871         if ((ddl=ques.delegateDAO.read(trans, rd.value)).notOKorIsEmpty()) {
3872             return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3873         }
3874         final DelegateDAO.Data dd = ddl.value.get(0);
3875         Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3876         if (rv.notOK()) {
3877             return rv;
3878         }
3879         
3880         return ques.delegateDAO.delete(trans, dd, false);
3881     }
3882
3883     @Override
3884     public Result<Void> deleteDelegate(AuthzTrans trans, String userName) {
3885         DelegateDAO.Data dd = new DelegateDAO.Data();
3886         final Validator v = new ServiceValidator();
3887         if (v.nullOrBlank("User", userName).err()) {
3888             return Result.err(Status.ERR_BadData,v.errs());
3889         }
3890         dd.user = userName;
3891         Result<List<DelegateDAO.Data>> ddl;
3892         if ((ddl=ques.delegateDAO.read(trans, dd)).notOKorIsEmpty()) {
3893             return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3894         }
3895         dd = ddl.value.get(0);
3896         Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3897         if (rv.notOK()) {
3898             return rv;
3899         }
3900         
3901         return ques.delegateDAO.delete(trans, dd, false);
3902     }
3903     
3904     @Override
3905     public Result<DELGS> getDelegatesByUser(AuthzTrans trans, String user) {
3906         final Validator v = new ServiceValidator();
3907         if (v.nullOrBlank("User", user).err()) {
3908             return Result.err(Status.ERR_BadData,v.errs());
3909         }
3910
3911         DelegateDAO.Data ddd = new DelegateDAO.Data();
3912         ddd.user = user;
3913         ddd.delegate = null;
3914         Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3915         if (rv.notOK()) {
3916             return Result.err(rv);
3917         }
3918         
3919         TimeTaken tt = trans.start("Get delegates for a user", Env.SUB);
3920
3921         Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.read(trans, user);
3922         try {
3923             if (dbDelgs.isOKhasData()) {
3924                 return mapper.delegate(dbDelgs.value);
3925             } else {
3926                 return Result.err(Status.ERR_DelegateNotFound,"No Delegate found for [%s]",user);
3927             }
3928         } finally {
3929             tt.done();
3930         }        
3931     }
3932
3933     @Override
3934     public Result<DELGS> getDelegatesByDelegate(AuthzTrans trans, String delegate) {
3935         final Validator v = new ServiceValidator();
3936         if (v.nullOrBlank("Delegate", delegate).err()) {
3937             return Result.err(Status.ERR_BadData,v.errs());
3938         }
3939
3940         DelegateDAO.Data ddd = new DelegateDAO.Data();
3941         ddd.user = delegate;
3942         Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3943         if (rv.notOK()) {
3944             return Result.err(rv);
3945         }
3946
3947         TimeTaken tt = trans.start("Get users for a delegate", Env.SUB);
3948
3949         Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.readByDelegate(trans, delegate);
3950         try {
3951             if (dbDelgs.isOKhasData()) {
3952                 return mapper.delegate(dbDelgs.value);
3953             } else {
3954                 return Result.err(Status.ERR_DelegateNotFound,"Delegate [%s] is not delegating for anyone.",delegate);
3955             }
3956         } finally {
3957             tt.done();
3958         }        
3959     }
3960
3961 /***********************************
3962  * APPROVAL 
3963  ***********************************/
3964     private static final String APPR_FMT = "actor=%s, action=%s, operation=\"%s\", requestor=%s, delegator=%s";
3965     @Override
3966     public Result<Void> updateApproval(AuthzTrans trans, APPROVALS approvals) {
3967         Result<List<ApprovalDAO.Data>> rlad = mapper.approvals(approvals);
3968         if (rlad.notOK()) {
3969             return Result.err(rlad);
3970         }
3971         int numApprs = rlad.value.size();
3972         if (numApprs<1) {
3973             return Result.err(Status.ERR_NoApprovals,"No Approvals sent for Updating");
3974         }
3975         int numProcessed = 0;
3976         String user = trans.user();
3977         
3978         Result<List<ApprovalDAO.Data>> curr;
3979         Lookup<List<ApprovalDAO.Data>> apprByTicket=null;
3980         for (ApprovalDAO.Data updt : rlad.value) {
3981             if (updt.ticket!=null) {
3982                 curr = ques.approvalDAO.readByTicket(trans, updt.ticket);
3983                 if (curr.isOKhasData()) {
3984                     final List<ApprovalDAO.Data> add = curr.value;
3985                     // Store a Pre-Lookup
3986                     apprByTicket = (trans1, noop) -> add;
3987                 }
3988             } else if (updt.id!=null) {
3989                 curr = ques.approvalDAO.read(trans, updt);
3990             } else if (updt.approver!=null) {
3991                 curr = ques.approvalDAO.readByApprover(trans, updt.approver);
3992             } else {
3993                 return Result.err(Status.ERR_BadData,"Approvals need ID, Ticket or Approval data to update");
3994             }
3995
3996             if (curr.isOKhasData()) {
3997                 Map<String, Result<List<DelegateDAO.Data>>> delegateCache = new HashMap<>();
3998                 Map<UUID, FutureDAO.Data> futureCache = new HashMap<>();
3999                 FutureDAO.Data hasDeleted = new FutureDAO.Data();
4000                 
4001                 for (ApprovalDAO.Data cd : curr.value) {
4002                     if ("pending".equals(cd.status)) {
4003                         // Check for right record.  Need ID, or (Ticket&Trans.User==Appr)
4004                         // If Default ID
4005                         boolean delegatedAction = ques.isDelegated(trans, user, cd.approver, delegateCache);
4006                         String delegator = cd.approver;
4007                         if (updt.id!=null || 
4008                             (updt.ticket!=null && user.equals(cd.approver)) ||
4009                             (updt.ticket!=null && delegatedAction)) {
4010                             if (updt.ticket.equals(cd.ticket)) {
4011                                 Changed ch = new Changed();
4012                                 cd.id = ch.changed(cd.id,updt.id);
4013 //                                cd.ticket = changed(cd.ticket,updt.ticket);
4014                                 cd.user = ch.changed(cd.user,updt.user);
4015                                 cd.approver = ch.changed(cd.approver,updt.approver);
4016                                 cd.type = ch.changed(cd.type,updt.type);
4017                                 cd.status = ch.changed(cd.status,updt.status);
4018                                 cd.memo = ch.changed(cd.memo,updt.memo);
4019                                 cd.operation = ch.changed(cd.operation,updt.operation);
4020                                 cd.updated = ch.changed(cd.updated,updt.updated==null?new Date():updt.updated);
4021 //                                if (updt.status.equals("denied")) {
4022 //                                    cd.last_notified = null;
4023 //                                }
4024                                 if (cd.ticket!=null) {
4025                                     FutureDAO.Data fdd = futureCache.get(cd.ticket);
4026                                     if (fdd==null) { // haven't processed ticket yet
4027                                         Result<FutureDAO.Data> rfdd = ques.futureDAO.readPrimKey(trans, cd.ticket);
4028                                         if (rfdd.isOK()) {
4029                                             fdd = rfdd.value; // null is ok
4030                                         } else {
4031                                             fdd = hasDeleted;
4032                                         }
4033                                         futureCache.put(cd.ticket, fdd); // processed this Ticket... don't do others on this ticket
4034                                     }
4035                                     if (fdd==hasDeleted) { // YES, by Object
4036                                         cd.ticket = null;
4037                                         cd.status = "ticketDeleted";
4038                                         ch.hasChanged(true);
4039                                     } else {
4040                                         FUTURE_OP fop = FUTURE_OP.toFO(cd.operation);
4041                                         if (fop==null) {
4042                                             trans.info().printf("Approval Status %s is not actionable",cd.status);
4043                                         } else if (apprByTicket!=null) {
4044                                             Result<OP_STATUS> rv = func.performFutureOp(trans, fop, fdd, apprByTicket,func.urDBLookup);
4045                                             if (rv.isOK()) {
4046                                                 switch(rv.value) {
4047                                                     case E:
4048                                                         if (delegatedAction) {
4049                                                             trans.audit().printf(APPR_FMT,user,updt.status,cd.memo,cd.user,delegator);
4050                                                         }
4051                                                         futureCache.put(cd.ticket, hasDeleted);
4052                                                         break;
4053                                                     case D:
4054                                                     case L:
4055                                                         ch.hasChanged(true);
4056                                                         trans.audit().printf(APPR_FMT,user,rv.value.desc(),cd.memo,cd.user,delegator);
4057                                                         futureCache.put(cd.ticket, hasDeleted);
4058                                                         break;
4059                                                     default:
4060                                                 }
4061                                             } else {
4062                                                 trans.info().log(rv.toString());
4063                                             }
4064                                         }
4065
4066                                     }
4067                                     ++numProcessed;
4068                                 }
4069                                 if (ch.hasChanged()) {
4070                                     ques.approvalDAO.update(trans, cd, true);
4071                                 }
4072                             }
4073                         }
4074                     }
4075                 }
4076             }
4077         }
4078
4079         if (numApprs==numProcessed) {
4080             return Result.ok();
4081         }
4082         return Result.err(Status.ERR_ActionNotCompleted,numProcessed + " out of " + numApprs + " completed");
4083
4084     }
4085     
4086     private static class Changed {
4087         private boolean hasChanged = false;
4088
4089         public<T> T changed(T src, T proposed) {
4090             if (proposed==null || (src!=null && src.equals(proposed))) {
4091                 return src;
4092             }
4093             hasChanged=true;
4094             return proposed;
4095         }
4096
4097         public void hasChanged(boolean b) {
4098             hasChanged=b;
4099         }
4100
4101         public boolean hasChanged() {
4102             return hasChanged;
4103         }
4104     }
4105
4106     @Override
4107     public Result<APPROVALS> getApprovalsByUser(AuthzTrans trans, String user) {
4108         final Validator v = new ServiceValidator();
4109         if (v.nullOrBlank("User", user).err()) { 
4110             return Result.err(Status.ERR_BadData,v.errs());
4111         }
4112
4113         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByUser(trans, user);
4114         if (rapd.isOK()) {
4115             return mapper.approvals(rapd.value);
4116         } else {
4117             return Result.err(rapd);
4118         }
4119 }
4120
4121     @Override
4122     public Result<APPROVALS> getApprovalsByTicket(AuthzTrans trans, String ticket) {
4123         final Validator v = new ServiceValidator();
4124         if (v.nullOrBlank("Ticket", ticket).err()) { 
4125             return Result.err(Status.ERR_BadData,v.errs());
4126         }
4127         UUID uuid;
4128         try {
4129             uuid = UUID.fromString(ticket);
4130         } catch (IllegalArgumentException e) {
4131             return Result.err(Status.ERR_BadData,e.getMessage());
4132         }
4133     
4134         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByTicket(trans, uuid);
4135         if (rapd.isOK()) {
4136             return mapper.approvals(rapd.value);
4137         } else {
4138             return Result.err(rapd);
4139         }
4140     }
4141     
4142     @Override
4143     public Result<APPROVALS> getApprovalsByApprover(AuthzTrans trans, String approver) {
4144         final Validator v = new ServiceValidator();
4145         if (v.nullOrBlank("Approver", approver).err()) {
4146             return Result.err(Status.ERR_BadData,v.errs());
4147         }
4148         
4149         List<ApprovalDAO.Data> listRapds = new ArrayList<>();
4150         
4151         Result<List<ApprovalDAO.Data>> myRapd = ques.approvalDAO.readByApprover(trans, approver);
4152         if (myRapd.notOK()) {
4153             return Result.err(myRapd);
4154         }
4155         
4156         listRapds.addAll(myRapd.value);
4157         
4158         Result<List<DelegateDAO.Data>> delegatedFor = ques.delegateDAO.readByDelegate(trans, approver);
4159         if (delegatedFor.isOK()) {
4160             for (DelegateDAO.Data dd : delegatedFor.value) {
4161                 if (dd.expires.after(new Date())) {
4162                     String delegator = dd.user;
4163                     Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByApprover(trans, delegator);
4164                     if (rapd.isOK()) {
4165                         for (ApprovalDAO.Data d : rapd.value) { 
4166                             if (!d.user.equals(trans.user())) {
4167                                 listRapds.add(d);
4168                             }
4169                         }
4170                     }
4171                 }
4172             }
4173         }
4174         
4175         return mapper.approvals(listRapds);
4176     }
4177     
4178     /* (non-Javadoc)
4179      * @see org.onap.aaf.auth.service.AuthzService#clearCache(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
4180      */
4181     @Override
4182     public Result<Void> cacheClear(AuthzTrans trans, String cname) {
4183         if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4184             return ques.clearCache(trans,cname);
4185         }
4186         return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4187                 trans.user(),ROOT_NS,CACHE,cname);
4188     }
4189
4190     /* (non-Javadoc)
4191      * @see org.onap.aaf.auth.service.AuthzService#cacheClear(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.Integer)
4192      */
4193     @Override
4194     public Result<Void> cacheClear(AuthzTrans trans, String cname, int[] segment) {
4195         if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4196             Result<Void> v=null;
4197             for (int i: segment) {
4198                 v=ques.cacheClear(trans,cname,i);
4199             }
4200             if (v!=null) {
4201                 return v;
4202             }
4203         }
4204         return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4205                 trans.user(),ROOT_NS,CACHE,cname);
4206     }
4207
4208     /* (non-Javadoc)
4209      * @see org.onap.aaf.auth.service.AuthzService#dbReset(org.onap.aaf.auth.env.test.AuthzTrans)
4210      */
4211     @Override
4212     public void dbReset(AuthzTrans trans) {
4213         ques.historyDAO.reportPerhapsReset(trans, null);
4214     }
4215
4216 }
4217