Collection syntax change because of Sonar
[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