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