AT&T 2.0.19 Code drop, stage 3
[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@csp.att.com)" }
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@csp.att.com)" }
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@csp.att.com)" }
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@csp.att.com)",
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@csp.att.com)" 
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
538         @ApiDoc(
539                         method = GET,  
540                         path = "/authz/nss/either/:id",
541                         params = {      "id|string|true" },
542                         expectedCode = 200,
543                         errorCodes = { 403,404 }, 
544                         text = {        "Lists all Namespaces where Identity :id is either an Admin or an Owner", 
545                                                 "Note: :id must be fully qualified (i.e. ab1234@csp.att.com)" 
546                                         }
547                         )
548         @Override
549         public Result<NSS> getNSbyEither(AuthzTrans trans, String user, boolean full) {
550                 final Validator v = new ServiceValidator();
551                 if (v.nullOrBlank("User", user).err()) {
552                         return Result.err(Status.ERR_BadData, v.errs());
553                 }
554                 
555                 Result<Collection<Namespace>> rn = loadNamepace(trans, user, null, full);
556                 if(rn.notOK()) {
557                         return Result.err(rn);
558                 }
559                 if (rn.isEmpty()) {
560                         return Result.err(Status.ERR_NotFound, "[%s] is not an admin or owner for any namespaces",user);                
561                 }
562                 NSS nss = mapper.newInstance(API.NSS);
563                 // Note: "loadNamespace" already validates view of Namespace
564                 return mapper.nss(trans, rn.value, nss);
565         }
566
567         private Result<Collection<Namespace>> loadNamepace(AuthzTrans trans, String user, String endsWith, boolean full) {
568                 Result<List<UserRoleDAO.Data>> urd = ques.userRoleDAO.readByUser(trans, user);
569                 if(urd.notOKorIsEmpty()) {
570                         return Result.err(urd);
571                 }
572                 Map<String, Namespace> lm = new HashMap<String,Namespace>();
573                 Map<String, Namespace> other = full || endsWith==null?null:new TreeMap<String,Namespace>();
574                 for(UserRoleDAO.Data urdd : urd.value) {
575                         if(full) {
576                                 if(endsWith==null || urdd.role.endsWith(endsWith)) {
577                                         RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
578                                         Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
579                                         if(nsd.isOK()) {
580                                                 Namespace namespace = lm.get(nsd.value.name);
581                                                 if(namespace==null) {
582                                                         namespace = new Namespace(nsd.value);
583                                                         lm.put(namespace.name,namespace);
584                                                 }
585                                                 Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
586                                                 if(rls.isOK()) {
587                                                         namespace.admin=rls.value;
588                                                 }
589                                                 
590                                                 rls = func.getOwners(trans, namespace.name, false);
591                                                 if(rls.isOK()) {
592                                                         namespace.owner=rls.value;
593                                                 }
594                                         }
595                                 }
596                         } else { // Shortened version.  Only Namespace Info available from Role.
597                                 if(Question.ADMIN.equals(urdd.rname) || Question.OWNER.equals(urdd.rname)) {
598                                         RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
599                                         Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
600                                         if(nsd.isOK()) {
601                                                 Namespace namespace = lm.get(nsd.value.name);
602                                                 if(namespace==null) {
603                                                         if(other!=null) {
604                                                                 namespace = other.remove(nsd.value.name);
605                                                         }
606                                                         if(namespace==null) {
607                                                                 namespace = new Namespace(nsd.value);
608                                                                 namespace.admin=new ArrayList<String>();
609                                                                 namespace.owner=new ArrayList<String>();
610                                                         }
611                                                         if(endsWith==null || urdd.role.endsWith(endsWith)) {
612                                                                 lm.put(namespace.name,namespace);
613                                                         } else { 
614                                                                 other.put(namespace.name,namespace);
615                                                         }
616                                                 }
617                                                 if(Question.OWNER.equals(urdd.rname)) {
618                                                         namespace.owner.add(urdd.user);
619                                                 } else {
620                                                         namespace.admin.add(urdd.user);
621                                                 }
622                                         }
623                                 }
624                         }
625                 }
626                 return Result.ok(lm.values());
627         }
628
629         @ApiDoc(
630                         method = GET,  
631                         path = "/authz/nss/responsible/:id",
632                         params = {      "id|string|true" },
633                         expectedCode = 200,
634                         errorCodes = { 403,404 }, 
635                         text = {        "Lists all Namespaces where Identity :id is a Responsible Party", 
636                                                 "Note: :id must be fully qualified (i.e. ab1234@csp.att.com)"
637                                         }
638                         )
639         @Override
640         public Result<NSS> getNSbyResponsible(AuthzTrans trans, String user, boolean full) {
641                 final Validator v = new ServiceValidator();
642                 if (v.nullOrBlank("User", user).err()) {
643                         return Result.err(Status.ERR_BadData, v.errs());
644                 }
645                 Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".owner",full);
646                 if(rn.notOK()) {
647                         return Result.err(rn);
648                 }
649                 if (rn.isEmpty()) {
650                         return Result.err(Status.ERR_NotFound, "[%s] is not an owner for any namespaces",user);         
651                 }
652                 NSS nss = mapper.newInstance(API.NSS);
653                 // Note: "loadNamespace" prevalidates
654                 return mapper.nss(trans, rn.value, nss);
655         }
656         
657         @ApiDoc(
658                         method = GET,  
659                         path = "/authz/nss/children/:id",
660                         params = {      "id|string|true" },
661                         expectedCode = 200,
662                         errorCodes = { 403,404 }, 
663                         text = {        "Lists all Child Namespaces of Namespace :id", 
664                                                 "Note: This is not a cached read"
665                                         }
666                         )
667         @Override
668         public Result<NSS> getNSsChildren(AuthzTrans trans, String parent) {
669                 final Validator v = new ServiceValidator();
670                 if(v.nullOrBlank("NS", parent).err())  {
671                         return Result.err(Status.ERR_BadData,v.errs());
672                 }
673                 
674                 Result<NsDAO.Data> rnd = ques.deriveNs(trans, parent);
675                 if(rnd.notOK()) {
676                         return Result.err(rnd);
677                 }
678                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
679                 if(rnd.notOK()) {
680                         return Result.err(rnd); 
681                 }
682
683                 Set<Namespace> lm = new HashSet<Namespace>();
684                 Result<List<NsDAO.Data>> rlnd = ques.nsDAO.dao().getChildren(trans, parent);
685                 if(rlnd.isOK()) {
686                         if(rlnd.isEmpty()) {
687                                 return Result.err(Status.ERR_NotFound, "No data found for %s",parent);
688                         }
689                         for(NsDAO.Data ndd : rlnd.value) {
690                                 Namespace namespace = new Namespace(ndd);
691                                 Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
692                                 if(rls.isOK()) {
693                                         namespace.admin=rls.value;
694                                 }
695                                 
696                                 rls = func.getOwners(trans, namespace.name, false);
697                                 if(rls.isOK()) {
698                                         namespace.owner=rls.value;
699                                 }
700
701                                 lm.add(namespace);
702                         }
703                         NSS nss = mapper.newInstance(API.NSS);
704                         return mapper.nss(trans,lm, nss);
705                 } else {
706                         return Result.err(rlnd);
707                 }
708         }
709
710
711         @ApiDoc(
712                         method = PUT,  
713                         path = "/authz/ns",
714                         params = {},
715                         expectedCode = 200,
716                         errorCodes = { 403,404,406 }, 
717                         text = { "Replace the Current Description of a Namespace with a new one"
718                                         }
719                         )
720         @Override
721         public Result<Void> updateNsDescription(AuthzTrans trans, REQUEST from) {
722                 final Result<Namespace> nsd = mapper.ns(trans, from);
723                 final ServiceValidator v = new ServiceValidator();
724                 if(v.ns(nsd).err()) {
725                         return Result.err(Status.ERR_BadData,v.errs());
726                 }
727                 if(v.nullOrBlank("description", nsd.value.description).err()) {
728                         return Result.err(Status.ERR_BadData,v.errs());
729                 }
730
731                 Namespace namespace = nsd.value;
732                 Result<List<NsDAO.Data>> rlnd = ques.nsDAO.read(trans, namespace.name);
733                 
734                 if(rlnd.notOKorIsEmpty()) {
735                         return Result.err(Status.ERR_NotFound, "Namespace [%s] does not exist",namespace.name);
736                 }
737                 
738                 if (ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.write).notOK()) {
739                         return Result.err(Status.ERR_Denied, "You do not have approval to change %s",namespace.name);
740                 }
741
742                 Result<Void> rdr = ques.nsDAO.dao().addDescription(trans, namespace.name, namespace.description);
743                 if(rdr.isOK()) {
744                         return Result.ok();
745                 } else {
746                         return Result.err(rdr);
747                 }
748         }
749         
750         /**
751          * deleteNS
752          * @throws DAOException 
753          * @see org.onap.aaf.auth.service.AuthzService#deleteNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
754          */
755         @ApiDoc(
756                         method = DELETE,  
757                         path = "/authz/ns/:ns",
758                         params = {      "ns|string|true" },
759                         expectedCode = 200,
760                         errorCodes = { 403,404,424 }, 
761                         text = {        "Delete the Namespace :ns. Namespaces cannot normally be deleted when there ",
762                                                 "are still credentials associated with them, but they can be deleted by setting ",
763                                                 "the \"force\" property. To do this: Add 'force=true' as a query parameter",
764                                                 "<p>WARNING: Using force will delete all credentials attached to this namespace. Use with care.</p>"
765                                                 + "if the \"force\" property is set to 'force=move', then Permissions and Roles are not deleted,"
766                                                 + "but are retained, and assigned to the Parent Namespace.  'force=move' is not permitted "
767                                                 + "at or below Application Scope"
768                                                 }
769                         )
770         @Override
771         public Result<Void> deleteNS(AuthzTrans trans, String ns) {
772                 return func.deleteNS(trans, ns);
773         }
774
775
776 /***********************************
777  * PERM 
778  ***********************************/
779
780         /*
781          * (non-Javadoc)
782          * @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)
783          */
784         @ApiDoc( 
785                         method = POST,  
786                         path = "/authz/perm",
787                         params = {},
788                         expectedCode = 201,
789                         errorCodes = {403,404,406,409}, 
790                         text = { "Permission consists of:",
791                                          "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
792                                          + "is being protected</li>",
793                                          "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
794                                          + " instance of the type</li>",
795                                          "<li>action - what kind of action is allowed</li></ul>",
796                                          "Note: instance and action can be an *"
797                                          }
798                         )
799         @Override
800         public Result<Void> createPerm(final AuthzTrans trans,REQUEST rreq) {           
801                 final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
802                 final ServiceValidator v = new ServiceValidator();
803                 if(v.perm(newPd).err()) {
804                         return Result.err(Status.ERR_BadData,v.errs());
805                 }
806                 
807                 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, newPd.value,false,
808                         new Mapper.Memo() {
809                                 @Override
810                                 public String get() {
811                                         return "Create Permission [" + 
812                                                 newPd.value.fullType() + '|' + 
813                                                 newPd.value.instance + '|' + 
814                                                 newPd.value.action + ']';
815                                 }
816                         },
817                         new MayChange() {
818                                 private Result<NsDAO.Data> nsd;
819                                 @Override
820                                 public Result<?> mayChange() {
821                                         if(nsd==null) {
822                                                 nsd = ques.mayUser(trans, trans.user(), newPd.value, Access.write);
823                                         }
824                                         return nsd;
825                                 }
826                         });
827                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, newPd.value.ns);
828                 if(nsr.notOKorIsEmpty()) {
829                         return Result.err(nsr);
830                 }
831                 switch(fd.status) {
832                         case OK:
833                                 Result<String> rfc = func.createFuture(trans,fd.value, 
834                                                 newPd.value.fullType() + '|' + newPd.value.instance + '|' + newPd.value.action,
835                                                 trans.user(),
836                                                 nsr.value.get(0),
837                                                 FUTURE_OP.C);
838                                 if(rfc.isOK()) {
839                                         return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
840                                                         newPd.value.ns,
841                                                         newPd.value.type,
842                                                         newPd.value.instance,
843                                                         newPd.value.action);
844                                 } else {
845                                     return Result.err(rfc);
846                                 }
847                         case Status.ACC_Now:
848                                 return func.createPerm(trans, newPd.value, true);
849                         default:
850                                 return Result.err(fd);
851                 }       
852         }
853
854         @ApiDoc( 
855                         method = GET,  
856                         path = "/authz/perms/:type",
857                         params = {"type|string|true"},
858                         expectedCode = 200,
859                         errorCodes = { 404,406 }, 
860                         text = { "List All Permissions that match the :type element of the key" }
861                         )
862         @Override
863         public Result<PERMS> getPermsByType(AuthzTrans trans, final String permType) {
864                 final Validator v = new ServiceValidator();
865                 if(v.nullOrBlank("PermType", permType).err()) {
866                         return Result.err(Status.ERR_BadData,v.errs());
867                 }
868
869                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByType(trans, permType);
870                 if(rlpd.notOK()) {
871                         return Result.err(rlpd);
872                 }
873
874 //              We don't have instance & action for mayUserView... do we want to loop through all returned here as well as in mapper?
875 //              Result<NsDAO.Data> r;
876 //              if((r = ques.mayUserViewPerm(trans, trans.user(), permType)).notOK())return Result.err(r);
877                 
878                 PERMS perms = mapper.newInstance(API.PERMS);
879                 if(!rlpd.isEmpty()) {
880                         // Note: Mapper will restrict what can be viewed
881                         return mapper.perms(trans, rlpd.value, perms, true);
882                 }
883                 return Result.ok(perms);
884         }
885         
886         @ApiDoc( 
887                         method = GET,  
888                         path = "/authz/perms/:type/:instance/:action",
889                         params = {"type|string|true",
890                                           "instance|string|true",
891                                           "action|string|true"},
892                         expectedCode = 200,
893                         errorCodes = { 404,406 }, 
894                         text = { "List Permissions that match key; :type, :instance and :action" }
895                         )
896         @Override
897         public Result<PERMS> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
898                 final Validator v = new ServiceValidator();
899                 if(v.nullOrBlank("PermType", type).err()
900                                 || v.nullOrBlank("PermInstance", instance).err()
901                                 || v.nullOrBlank("PermAction", action).err()) {
902                         return Result.err(Status.ERR_BadData,v.errs());
903                 }
904                 
905                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByName(trans, type, instance, action);
906                 if(rlpd.notOK()) {
907                         return Result.err(rlpd);
908                 }
909
910                 PERMS perms = mapper.newInstance(API.PERMS);
911                 if(!rlpd.isEmpty()) {
912                         // Note: Mapper will restrict what can be viewed
913                         return mapper.perms(trans, rlpd.value, perms, true);
914                 }
915                 return Result.ok(perms);
916         }
917
918         @ApiDoc( 
919                         method = GET,  
920                         path = "/authz/perms/user/:user",
921                         params = {"user|string|true"},
922                         expectedCode = 200,
923                         errorCodes = { 404,406 }, 
924                         text = { "List All Permissions that match user :user",
925                                          "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>"}
926                         )
927         @Override
928         public Result<PERMS> getPermsByUser(AuthzTrans trans, String user) {
929                 final Validator v = new ServiceValidator();
930                 if(v.nullOrBlank("User", user).err()) {
931                         return Result.err(Status.ERR_BadData,v.errs());
932                 }
933
934                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByUser(trans, user, 
935                                 trans.requested(force));
936                 if(rlpd.notOK()) {
937                         return Result.err(rlpd);
938                 }
939                 
940                 PERMS perms = mapper.newInstance(API.PERMS);
941                 
942                 if(rlpd.isEmpty()) {
943                         return Result.ok(perms);
944                 }
945                 // Note: Mapper will restrict what can be viewed
946                 //   if user is the same as that which is looked up, no filtering is required
947                 return mapper.perms(trans, rlpd.value, 
948                                 perms, 
949                                 !user.equals(trans.user()));
950         }
951
952         @ApiDoc( 
953                         method = GET,  
954                         path = "/authz/perms/user/:user/scope/:scope",
955                         params = {"user|string|true","scope|string|true"},
956                         expectedCode = 200,
957                         errorCodes = { 404,406 }, 
958                         text = { "List All Permissions that match user :user, filtered by NS (Scope)",
959                                          "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
960                                          "<p>'scope' must be expressed as NSs separated by ':'</p>"
961                                         }
962                         )
963         @Override
964         public Result<PERMS> getPermsByUserScope(AuthzTrans trans, String user, String[] scopes) {
965                 final Validator v = new ServiceValidator();
966                 if(v.nullOrBlank("User", user).err()) {
967                         return Result.err(Status.ERR_BadData,v.errs());
968                 }
969
970                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByUser(trans, user, trans.requested(force));
971                 if(rlpd.notOK()) {
972                         return Result.err(rlpd);
973                 }
974                 
975                 PERMS perms = mapper.newInstance(API.PERMS);
976                 
977                 if(rlpd.isEmpty()) {
978                         return Result.ok(perms);
979                 }
980                 // Note: Mapper will restrict what can be viewed
981                 //   if user is the same as that which is looked up, no filtering is required
982                 return mapper.perms(trans, rlpd.value, 
983                                 perms, 
984                                 scopes,
985                                 !user.equals(trans.user()));
986         }
987
988         @ApiDoc( 
989                         method = POST,  
990                         path = "/authz/perms/user/:user",
991                         params = {"user|string|true"},
992                         expectedCode = 200,
993                         errorCodes = { 404,406 }, 
994                         text = { "List All Permissions that match user :user",
995                                          "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
996                                          "",
997                                          "Present Queries as one or more Permissions (see ContentType Links below for format).",
998                                          "",
999                                          "If the Caller is Granted this specific Permission, and the Permission is valid",
1000                                          "  for the User, it will be included in response Permissions, along with",
1001                                          "  all the normal permissions on the 'GET' version of this call.  If it is not",
1002                                          "  valid, or Caller does not have permission to see, it will be removed from the list",
1003                                          "",
1004                                          "  *Note: This design allows you to make one call for all expected permissions",
1005                                          " The permission to be included MUST be:",
1006                                          "     <user namespace>.access|:<ns|role|perm>[:key]|<create|read|write>",
1007                                          "   examples:",
1008                                          "     com.att.myns.access|:ns|write",
1009                                          "     com.att.myns.access|:role:myrole|create",
1010                                          "     com.att.myns.access|:perm:mytype:myinstance:myaction|read",
1011                                          ""
1012                                          }
1013                         )
1014         @Override
1015         public Result<PERMS> getPermsByUser(AuthzTrans trans, PERMS _perms, String user) {
1016                 PERMS perms = _perms;
1017                 final Validator v = new ServiceValidator();
1018                 if(v.nullOrBlank("User", user).err()) {
1019                         return Result.err(Status.ERR_BadData,v.errs());
1020                 }
1021                 
1022                 //////////////
1023                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByUser(trans, user,trans.requested(force));
1024                 if(rlpd.notOK()) {
1025                         return Result.err(rlpd);
1026                 }
1027                 
1028                 /*//TODO 
1029                   1) See if allowed to query
1030                   2) See if User is allowed
1031                   */
1032                 Result<List<PermDAO.Data>> in = mapper.perms(trans, perms);
1033                 if(in.isOKhasData()) {
1034                         List<PermDAO.Data> out = rlpd.value;
1035                         boolean ok;
1036                         for(PermDAO.Data pdd : in.value) {
1037                                 ok = false;
1038                                 if("access".equals(pdd.type)) {
1039                                         Access access = Access.valueOf(pdd.action);
1040                                         String[] mdkey = Split.splitTrim(':',pdd.instance);
1041                                         if(mdkey.length>1) {
1042                                                 String type = mdkey[1];
1043                                                 if("role".equals(type)) {
1044                                                         if(mdkey.length>2) {
1045                                                                 RoleDAO.Data rdd = new RoleDAO.Data();
1046                                                                 rdd.ns=pdd.ns;
1047                                                                 rdd.name=mdkey[2];
1048                                                                 ok = ques.mayUser(trans, trans.user(), rdd, Access.read).isOK() && ques.mayUser(trans, user, rdd , access).isOK();
1049                                                         }
1050                                                 } else if("perm".equals(type)) {
1051                                                         if(mdkey.length>4) { // also need instance/action
1052                                                                 PermDAO.Data p = new PermDAO.Data();
1053                                                                 p.ns=pdd.ns;
1054                                                                 p.type=mdkey[2];
1055                                                                 p.instance=mdkey[3];
1056                                                                 p.action=mdkey[4];
1057                                                                 ok = ques.mayUser(trans, trans.user(), p, Access.read).isOK() && ques.mayUser(trans, user, p , access).isOK();
1058                                                         }
1059                                                 } else if("ns".equals(type)) {
1060                                                         NsDAO.Data ndd = new NsDAO.Data();
1061                                                         ndd.name=pdd.ns;
1062                                                         ok = ques.mayUser(trans, trans.user(), ndd, Access.read).isOK() && ques.mayUser(trans, user, ndd , access).isOK();
1063                                                 }
1064                                         }
1065                                 }
1066                                 if(ok) {
1067                                         out.add(pdd);
1068                                 }
1069                         }
1070                 }               
1071                 
1072                 perms = mapper.newInstance(API.PERMS);
1073                 if(rlpd.isEmpty()) {
1074                         return Result.ok(perms);
1075                 }
1076                 // Note: Mapper will restrict what can be viewed
1077                 //   if user is the same as that which is looked up, no filtering is required
1078                 return mapper.perms(trans, rlpd.value, 
1079                                 perms, 
1080                                 !user.equals(trans.user()));
1081         }
1082         
1083         @ApiDoc( 
1084                         method = GET,  
1085                         path = "/authz/perms/role/:role",
1086                         params = {"role|string|true"},
1087                         expectedCode = 200,
1088                         errorCodes = { 404,406 }, 
1089                         text = { "List All Permissions that are granted to :role" }
1090                         )
1091         @Override
1092         public Result<PERMS> getPermsByRole(AuthzTrans trans,String role) {
1093                 final Validator v = new ServiceValidator();
1094                 if(v.nullOrBlank("Role", role).err()) {
1095                         return Result.err(Status.ERR_BadData,v.errs());
1096                 }
1097
1098                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques,role);
1099                 if(rrdd.notOK()) {
1100                         return Result.err(rrdd);
1101                 }
1102
1103                 Result<NsDAO.Data> r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
1104                 if(r.notOK()) {
1105                         return Result.err(r);
1106                 }
1107
1108                 PERMS perms = mapper.newInstance(API.PERMS);
1109
1110                 Result<List<PermDAO.Data>> rlpd = ques.getPermsByRole(trans, role, trans.requested(force));
1111                 if(rlpd.isOKhasData()) {
1112                         // Note: Mapper will restrict what can be viewed
1113                         return mapper.perms(trans, rlpd.value, perms, true);
1114                 }
1115                 return Result.ok(perms);
1116         }
1117
1118         @ApiDoc( 
1119                         method = GET,  
1120                         path = "/authz/perms/ns/:ns",
1121                         params = {"ns|string|true"},
1122                         expectedCode = 200,
1123                         errorCodes = { 404,406 }, 
1124                         text = { "List All Permissions that are in Namespace :ns" }
1125                         )
1126         @Override
1127         public Result<PERMS> getPermsByNS(AuthzTrans trans,String ns) {
1128                 final Validator v = new ServiceValidator();
1129                 if(v.nullOrBlank("NS", ns).err()) {
1130                         return Result.err(Status.ERR_BadData,v.errs());
1131                 }
1132
1133                 Result<NsDAO.Data> rnd = ques.deriveNs(trans, ns);
1134                 if(rnd.notOK()) {
1135                         return Result.err(rnd);
1136                 }
1137
1138                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
1139                 if(rnd.notOK()) {
1140                         return Result.err(rnd);         
1141                 }
1142                 
1143                 Result<List<PermDAO.Data>> rlpd = ques.permDAO.readNS(trans, ns);
1144                 if(rlpd.notOK()) {
1145                         return Result.err(rlpd);
1146                 }
1147
1148                 PERMS perms = mapper.newInstance(API.PERMS);
1149                 if(!rlpd.isEmpty()) {
1150                         // Note: Mapper will restrict what can be viewed
1151                         return mapper.perms(trans, rlpd.value,perms, true);
1152                 }
1153                 return Result.ok(perms);
1154         }
1155         
1156         @ApiDoc( 
1157                         method = PUT,  
1158                         path =  "/authz/perm/:type/:instance/:action",
1159                         params = {"type|string|true",
1160                                           "instance|string|true",
1161                                           "action|string|true"},
1162                         expectedCode = 200,
1163                         errorCodes = { 404,406, 409 }, 
1164                         text = { "Rename the Permission referenced by :type :instance :action, and "
1165                                         + "rename (copy/delete) to the Permission described in PermRequest" }
1166                         )
1167         @Override
1168         public Result<Void> renamePerm(final AuthzTrans trans,REQUEST rreq, String origType, String origInstance, String origAction) {
1169                 final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
1170                 final ServiceValidator v = new ServiceValidator();
1171                 if(v.perm(newPd).err()) {
1172                         return Result.err(Status.ERR_BadData,v.errs());
1173                 }
1174
1175                 if (ques.mayUser(trans, trans.user(), newPd.value,Access.write).notOK()) {
1176                         return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1177                                         newPd.value.ns,newPd.value.type,newPd.value.instance,newPd.value.action);
1178                 }
1179                 
1180                 Result<NsSplit> nss = ques.deriveNsSplit(trans, origType);
1181                 Result<List<PermDAO.Data>> origRlpd = ques.permDAO.read(trans, nss.value.ns, nss.value.name, origInstance, origAction); 
1182                 
1183                 if(origRlpd.notOKorIsEmpty()) {
1184                         return Result.err(Status.ERR_PermissionNotFound, 
1185                                         "Permission [%s|%s|%s] does not exist",
1186                                         origType,origInstance,origAction);
1187                 }
1188                 
1189                 PermDAO.Data origPd = origRlpd.value.get(0);
1190
1191                 if (!origPd.ns.equals(newPd.value.ns)) {
1192                         return Result.err(Status.ERR_Denied, "Cannot change namespace with rename command. " +
1193                                         "<new type> must start with [" + origPd.ns + "]");
1194                 }
1195                 
1196                 if ( origPd.type.equals(newPd.value.type) && 
1197                                 origPd.action.equals(newPd.value.action) && 
1198                                 origPd.instance.equals(newPd.value.instance) ) {
1199                         return Result.err(Status.ERR_ConflictAlreadyExists, "New Permission must be different than original permission");
1200                 }
1201                 
1202                 Set<String> origRoles = origPd.roles(false);
1203                 if (!origRoles.isEmpty()) {
1204                         Set<String> roles = newPd.value.roles(true);
1205                         for (String role : origPd.roles) {
1206                                 roles.add(role); 
1207                         }
1208                 }       
1209                 
1210                 newPd.value.description = origPd.description;
1211                 
1212                 Result<Void> rv = null;
1213                 
1214                 rv = func.createPerm(trans, newPd.value, false);
1215                 if (rv.isOK()) {
1216                         rv = func.deletePerm(trans, origPd, true, false);
1217                 }
1218                 return rv;
1219         }
1220         
1221         @ApiDoc( 
1222                         method = PUT,  
1223                         path = "/authz/perm",
1224                         params = {},
1225                         expectedCode = 200,
1226                         errorCodes = { 404,406 }, 
1227                         text = { "Add Description Data to Perm" }
1228                         )
1229         @Override
1230         public Result<Void> updatePermDescription(AuthzTrans trans, REQUEST from) {
1231                 final Result<PermDAO.Data> pd = mapper.perm(trans, from);
1232                 final ServiceValidator v = new ServiceValidator();
1233                 if(v.perm(pd).err()) {
1234                         return Result.err(Status.ERR_BadData,v.errs());
1235                 }
1236                 if(v.nullOrBlank("description", pd.value.description).err()) {
1237                         return Result.err(Status.ERR_BadData,v.errs());
1238                 }
1239                 final PermDAO.Data perm = pd.value;
1240                 if(ques.permDAO.read(trans, perm.ns, perm.type, perm.instance,perm.action).notOKorIsEmpty()) {
1241                         return Result.err(Status.ERR_NotFound, "Permission [%s.%s|%s|%s] does not exist",
1242                                 perm.ns,perm.type,perm.instance,perm.action);
1243                 }
1244
1245                 if (ques.mayUser(trans, trans.user(), perm, Access.write).notOK()) {
1246                         return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1247                                         perm.ns,perm.type,perm.instance,perm.action);
1248                 }
1249
1250                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, pd.value.ns);
1251                 if(nsr.notOKorIsEmpty()) {
1252                         return Result.err(nsr);
1253                 }
1254
1255                 Result<Void> rdr = ques.permDAO.addDescription(trans, perm.ns, perm.type, perm.instance,
1256                                 perm.action, perm.description);
1257                 if(rdr.isOK()) {
1258                         return Result.ok();
1259                 } else {
1260                         return Result.err(rdr);
1261                 }
1262
1263         }
1264         
1265     @ApiDoc(
1266             method = PUT,
1267             path = "/authz/role/perm",
1268             params = {},
1269             expectedCode = 201,
1270             errorCodes = {403,404,406,409},
1271             text = { "Set a permission's roles to roles given" }
1272            )
1273
1274         @Override
1275         public Result<Void> resetPermRoles(final AuthzTrans trans, REQUEST rreq) {
1276                 final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
1277                 if(updt.notOKorIsEmpty()) {
1278                         return Result.err(updt);
1279                 }
1280
1281                 final ServiceValidator v = new ServiceValidator();
1282                 if(v.perm(updt).err()) {
1283                         return Result.err(Status.ERR_BadData,v.errs());
1284                 }
1285
1286                 Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), updt.value, Access.write);
1287                 if (nsd.notOK()) {
1288                         return Result.err(nsd);
1289                 }
1290
1291                 // Read full set to get CURRENT values
1292                 Result<List<PermDAO.Data>> rcurr = ques.permDAO.read(trans, 
1293                                 updt.value.ns, 
1294                                 updt.value.type, 
1295                                 updt.value.instance, 
1296                                 updt.value.action);
1297                 
1298                 if(rcurr.notOKorIsEmpty()) {
1299                         return Result.err(Status.ERR_PermissionNotFound, 
1300                                         "Permission [%s.%s|%s|%s] does not exist",
1301                                          updt.value.ns,updt.value.type,updt.value.instance,updt.value.action);
1302                 }
1303                 
1304                 // Create a set of Update Roles, which are in Internal Format
1305                 Set<String> updtRoles = new HashSet<String>();
1306                 Result<NsSplit> nss;
1307                 for(String role : updt.value.roles(false)) {
1308                         nss = ques.deriveNsSplit(trans, role);
1309                         if(nss.isOK()) {
1310                                 updtRoles.add(nss.value.ns + '|' + nss.value.name);
1311                         } else {
1312                                 trans.error().log(nss.errorString());
1313                         }
1314                 }
1315
1316                 Result<Void> rv = null;
1317                 
1318                 for(PermDAO.Data curr : rcurr.value) {
1319                         Set<String> currRoles = curr.roles(false);
1320                         // must add roles to this perm, and add this perm to each role 
1321                         // in the update, but not in the current                        
1322                         for (String role : updtRoles) {
1323                                 if (!currRoles.contains(role)) {
1324                                         Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1325                                         if(key.isOKhasData()) {
1326                                                 Result<List<RoleDAO.Data>> rrd = ques.roleDAO.read(trans, key.value);
1327                                                 if(rrd.isOKhasData()) {
1328                                                         for(RoleDAO.Data r : rrd.value) {
1329                                                                 rv = func.addPermToRole(trans, r, curr, false);
1330                                                                 if (rv.notOK() && rv.status!=Result.ERR_ConflictAlreadyExists) {
1331                                                                         return Result.err(rv);
1332                                                                 }
1333                                                         }
1334                                                 } else {
1335                                                         return Result.err(rrd);
1336                                                 }
1337                                         }
1338                                 }
1339                         }
1340                         // similarly, must delete roles from this perm, and delete this perm from each role
1341                         // in the update, but not in the current
1342                         for (String role : currRoles) {
1343                                 if (!updtRoles.contains(role)) {
1344                                         Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1345                                         if(key.isOKhasData()) {
1346                                                 Result<List<RoleDAO.Data>> rdd = ques.roleDAO.read(trans, key.value);
1347                                                 if(rdd.isOKhasData()) {
1348                                                         for(RoleDAO.Data r : rdd.value) {
1349                                                                 rv = func.delPermFromRole(trans, r, curr, true);
1350                                                                 if (rv.notOK() && rv.status!=Status.ERR_PermissionNotFound) {
1351                                                                         return Result.err(rv);
1352                                                                 }
1353                                                         }
1354                                                 }
1355                                         }
1356                                 }
1357                         }                               
1358                 } 
1359                 return rv==null?Result.ok():rv;         
1360         }
1361         
1362         @ApiDoc( 
1363                         method = DELETE,
1364                         path = "/authz/perm",
1365                         params = {},
1366                         expectedCode = 200,
1367                         errorCodes = { 404,406 }, 
1368                         text = { "Delete the Permission referenced by PermKey.",
1369                                         "You cannot normally delete a permission which is still granted to roles,",
1370                                         "however the \"force\" property allows you to do just that. To do this: Add",
1371                                         "'force=true' as a query parameter.",
1372                                         "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>" }
1373                         )
1374         @Override
1375         public Result<Void> deletePerm(final AuthzTrans trans, REQUEST from) {
1376                 Result<PermDAO.Data> pd = mapper.perm(trans, from);
1377                 if(pd.notOK()) {
1378                         return Result.err(pd);
1379                 }
1380                 final ServiceValidator v = new ServiceValidator();
1381                 if(v.nullOrBlank(pd.value).err()) {
1382                         return Result.err(Status.ERR_BadData,v.errs());
1383                 }
1384                 final PermDAO.Data perm = pd.value;
1385                 if (ques.permDAO.read(trans, perm).notOKorIsEmpty()) {
1386                         return Result.err(Status.ERR_PermissionNotFound, "Permission [%s.%s|%s|%s] does not exist",
1387                                         perm.ns,perm.type,perm.instance,perm.action     );
1388                 }
1389
1390                 Result<FutureDAO.Data> fd = mapper.future(trans,PermDAO.TABLE,from,perm,false,
1391                                 new Mapper.Memo() {
1392                                         @Override
1393                                         public String get() {
1394                                                 return "Delete Permission [" + perm.fullPerm() + ']';
1395                                         }
1396                                 },
1397                         new MayChange() {
1398                                 private Result<NsDAO.Data> nsd;
1399                                 @Override
1400                                 public Result<?> mayChange() {
1401                                         if(nsd==null) {
1402                                                 nsd = ques.mayUser(trans, trans.user(), perm, Access.write);
1403                                         }
1404                                         return nsd;
1405                                 }
1406                         });
1407                 
1408                 switch(fd.status) {
1409                 case OK:
1410                         Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, perm.ns);
1411                         if(nsr.notOKorIsEmpty()) {
1412                                 return Result.err(nsr);
1413                         }
1414                         
1415                         Result<String> rfc = func.createFuture(trans, fd.value, 
1416                                         perm.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
1417                         if(rfc.isOK()) {
1418                                 return Result.err(Status.ACC_Future, "Perm Deletion [%s] is saved for future processing",perm.encode());
1419                         } else { 
1420                                 return Result.err(rfc);
1421                         }
1422                 case Status.ACC_Now:
1423                         return func.deletePerm(trans,perm,trans.requested(force), false);
1424                 default:
1425                         return Result.err(fd);
1426                 }                       
1427         }       
1428         
1429         @ApiDoc( 
1430                         method = DELETE,
1431                         path = "/authz/perm/:name/:type/:action",
1432                         params = {"type|string|true",
1433                                           "instance|string|true",
1434                                           "action|string|true"},
1435                         expectedCode = 200,
1436                         errorCodes = { 404,406 }, 
1437                         text = { "Delete the Permission referenced by :type :instance :action",
1438                                         "You cannot normally delete a permission which is still granted to roles,",
1439                                         "however the \"force\" property allows you to do just that. To do this: Add",
1440                                         "'force=true' as a query parameter",
1441                                         "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>"}
1442                         )
1443         @Override
1444         public Result<Void> deletePerm(AuthzTrans trans, String type, String instance, String action) {
1445                 final Validator v = new ServiceValidator();
1446                 if(v.nullOrBlank("Type",type)
1447                         .nullOrBlank("Instance",instance)
1448                         .nullOrBlank("Action",action)
1449                         .err()) {
1450                         return Result.err(Status.ERR_BadData,v.errs());
1451                 }
1452                 
1453                 Result<PermDAO.Data> pd = ques.permFrom(trans, type, instance, action);
1454                 if(pd.isOK()) {
1455                         return func.deletePerm(trans, pd.value, trans.requested(force), false);
1456                 } else {
1457                     return Result.err(pd);
1458                 }
1459         }
1460
1461 /***********************************
1462  * ROLE 
1463  ***********************************/
1464     @ApiDoc(
1465             method = POST,
1466             path = "/authz/role",
1467             params = {},
1468             expectedCode = 201,
1469             errorCodes = {403,404,406,409},
1470             text = {
1471
1472                 "Roles are part of Namespaces",
1473                 "Examples:",
1474                 "<ul><li>org.onap.aaf - The team that created and maintains AAF</li>",
1475                 "Roles do not include implied permissions for an App.  Instead, they contain explicit Granted Permissions by any Namespace in AAF (See Permissions)",
1476                 "Restrictions on Role Names:",
1477                 "<ul><li>Must start with valid Namespace name, terminated by . (dot/period)</li>",
1478                 "<li>Allowed Characters are a-zA-Z0-9._-</li>",
1479                 "<li>role names are Case Sensitive</li></ul>",
1480                 "The right questions to ask for defining and populating a Role in AAF, therefore, are:",
1481                 "<ul><li>'What Job Function does this represent?'</li>",
1482                 "<li>'Does this person perform this Job Function?'</li></ul>" }
1483            )
1484
1485         @Override
1486         public Result<Void> createRole(final AuthzTrans trans, REQUEST from) {
1487                 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1488                 final ServiceValidator v = new ServiceValidator();
1489                 if(v.role(rd).err()) {
1490                         return Result.err(Status.ERR_BadData,v.errs());
1491                 }
1492                 final RoleDAO.Data role = rd.value;
1493                 if(ques.roleDAO.read(trans, role.ns, role.name).isOKhasData()) {
1494                         return Result.err(Status.ERR_ConflictAlreadyExists, "Role [" + role.fullName() + "] already exists");
1495                 }
1496
1497                 Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
1498                         new Mapper.Memo() {
1499                                 @Override
1500                                 public String get() {
1501                                         return "Create Role [" + 
1502                                                 rd.value.fullName() + 
1503                                                 ']';
1504                                 }
1505                         },
1506                         new MayChange() {
1507                                 private Result<NsDAO.Data> nsd;
1508                                 @Override
1509                                 public Result<?> mayChange() {
1510                                         if(nsd==null) {
1511                                                 nsd = ques.mayUser(trans, trans.user(), role, Access.write);
1512                                         }
1513                                         return nsd;
1514                                 }
1515                         });
1516                 
1517                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rd.value.ns);
1518                 if(nsr.notOKorIsEmpty()) {
1519                         return Result.err(nsr);
1520                 }
1521
1522                 switch(fd.status) {
1523                         case OK:
1524                                 Result<String> rfc = func.createFuture(trans, fd.value, 
1525                                                 role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.C);
1526                                 if(rfc.isOK()) {
1527                                         return Result.err(Status.ACC_Future, "Role [%s.%s] is saved for future processing",
1528                                                         rd.value.ns,
1529                                                         rd.value.name);
1530                                 } else { 
1531                                         return Result.err(rfc);
1532                                 }
1533                         case Status.ACC_Now:
1534                                 Result<RoleDAO.Data> rdr = ques.roleDAO.create(trans, role);
1535                                 if(rdr.isOK()) {
1536                                         return Result.ok();
1537                                 } else {
1538                                         return Result.err(rdr);
1539                                 }
1540                         default:
1541                                 return Result.err(fd);
1542                 }
1543         }
1544
1545         /* (non-Javadoc)
1546          * @see org.onap.aaf.auth.service.AuthzService#getRolesByName(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1547          */
1548     @ApiDoc(
1549             method = GET,
1550             path = "/authz/roles/:role",
1551             params = {"role|string|true"}, 
1552             expectedCode = 200,
1553             errorCodes = {404,406},
1554             text = { "List Roles that match :role",
1555                          "Note: You must have permission to see any given role"
1556                    }
1557            )
1558         @Override
1559         public Result<ROLES> getRolesByName(AuthzTrans trans, String role) {
1560                 final Validator v = new ServiceValidator();
1561                 if(v.nullOrBlank("Role", role).err()) {
1562                         return Result.err(Status.ERR_BadData,v.errs());
1563                 }
1564                 
1565                 // Determine if User can ask this question
1566                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
1567                 if(rrdd.isOKhasData()) {
1568                         Result<NsDAO.Data> r;
1569                         if((r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read)).notOK()) {
1570                                 return Result.err(r);
1571                         }
1572                 } else {
1573                         return Result.err(rrdd);
1574                 }
1575                 
1576                 // Look up data
1577                 Result<List<RoleDAO.Data>> rlrd = ques.getRolesByName(trans, role);
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,null)
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*1000;
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.isOK()) {
2917                                         return Result.ok();
2918                                 }
2919                                 return Result.err(udr);
2920                         default:
2921                                 return Result.err(fd);
2922                 }
2923         
2924         }
2925
2926
2927         @Override
2928         public Result<Date> doesCredentialMatch(AuthzTrans trans, REQUEST credReq) {
2929                 TimeTaken tt = trans.start("Does Credential Match", Env.SUB);
2930                 try {
2931                         // Note: Mapper assigns RAW type
2932                         Result<CredDAO.Data> data = mapper.cred(trans, credReq,false);
2933                         if(data.notOKorIsEmpty()) {
2934                                 return Result.err(data);
2935                         }
2936                         CredDAO.Data cred = data.value; // of the Mapped Cred
2937                         if(cred.cred==null) {
2938                                 return Result.err(Result.ERR_BadData,"No Password");
2939                         } else {
2940                                 return ques.doesUserCredMatch(trans, cred.id, cred.cred.array());
2941                         }
2942
2943                 } catch (DAOException e) {
2944                         trans.error().log(e,"Error looking up cred");
2945                         return Result.err(Status.ERR_Denied,"Credential does not match");
2946                 } finally {
2947                         tt.done();
2948                 }
2949         }
2950
2951         @ApiDoc( 
2952                         method = GET,  
2953                         path = "/authn/basicAuth",
2954                         params = {},
2955                         expectedCode = 200,
2956                         errorCodes = { 403 }, 
2957                         text = { "!!!! DEPRECATED without X509 Authentication STOP USING THIS API BY DECEMBER 2017, or use Certificates !!!!\n" 
2958                                         + "Use /authn/validate instead\n"
2959                                         + "Note: Validate a Password using BasicAuth Base64 encoded Header. This HTTP/S call is intended as a fast"
2960                                         + " User/Password lookup for Security Frameworks, and responds 200 if it passes BasicAuth "
2961                                 + "security, and 403 if it does not." }
2962                         )
2963         private void basicAuth() {
2964                 // This is a place holder for Documentation.  The real BasicAuth API does not call Service.
2965         }
2966         
2967         @ApiDoc( 
2968                         method = POST,  
2969                         path = "/authn/validate",
2970                         params = {},
2971                         expectedCode = 200,
2972                         errorCodes = { 403 }, 
2973                         text = { "Validate a Credential given a Credential Structure.  This is a more comprehensive validation, can "
2974                                         + "do more than BasicAuth as Credential types exp" }
2975                         )
2976         @Override
2977         public Result<Date> validateBasicAuth(AuthzTrans trans, String basicAuth) {
2978                 //TODO how to make sure people don't use this in browsers?  Do we care?
2979                 TimeTaken tt = trans.start("Validate Basic Auth", Env.SUB);
2980                 try {
2981                         BasicPrincipal bp = new BasicPrincipal(basicAuth,trans.org().getRealm());
2982                         Result<Date> rq = ques.doesUserCredMatch(trans, bp.getName(), bp.getCred());
2983                         // Note: Only want to log problem, don't want to send back to end user
2984                         if(rq.isOK()) {
2985                                 return rq;
2986                         } else {
2987                                 trans.audit().log(rq.errorString());
2988                         }
2989                 } catch (Exception e) {
2990                         trans.warn().log(e);
2991                 } finally {
2992                         tt.done();
2993                 }
2994                 return Result.err(Status.ERR_Denied,"Bad Basic Auth");
2995         }
2996
2997 /***********************************
2998  * USER-ROLE 
2999  ***********************************/
3000         @ApiDoc( 
3001                         method = POST,  
3002                         path = "/authz/userRole",
3003                         params = {},
3004                         expectedCode = 201,
3005                         errorCodes = {403,404,406,409}, 
3006                         text = { "Create a UserRole relationship (add User to Role)",
3007                                          "A UserRole is an object Representation of membership of a Role for limited time.",
3008                                          "If a shorter amount of time for Role ownership is required, use the 'End' field.",
3009                                          "** Note: Owners of Namespaces will be required to revalidate users in these roles ",
3010                                          "before Expirations expire.  Namespace owners will be notified by email."
3011                                    }
3012                         )
3013         @Override
3014         public Result<Void> createUserRole(final AuthzTrans trans, REQUEST from) {
3015                 TimeTaken tt = trans.start("Create UserRole", Env.SUB);
3016                 try {
3017                         Result<UserRoleDAO.Data> urr = mapper.userRole(trans, from);
3018                         if(urr.notOKorIsEmpty()) {
3019                                 return Result.err(urr);
3020                         }
3021                         final UserRoleDAO.Data userRole = urr.value;
3022                         
3023                         final ServiceValidator v = new ServiceValidator();
3024                         if(v.user_role(userRole).err() ||
3025                            v.user(trans.org(), userRole.user).err()) {
3026                                 return Result.err(Status.ERR_BadData,v.errs());
3027                         }
3028
3029
3030                          
3031                         // Check if user can change first
3032                         Result<FutureDAO.Data> fd = mapper.future(trans,UserRoleDAO.TABLE,from,urr.value,true, // may request Approvals
3033                                 new Mapper.Memo() {
3034                                         @Override
3035                                         public String get() {
3036                                                 return "Add User [" + userRole.user + "] to Role [" + 
3037                                                                 userRole.role + 
3038                                                                 ']';
3039                                         }
3040                                 },
3041                                 new MayChange() {
3042                                         private Result<NsDAO.Data> nsd;
3043                                         @Override
3044                                         public Result<?> mayChange() {
3045                                                 if(nsd==null) {
3046                                                         RoleDAO.Data r = RoleDAO.Data.decode(userRole);
3047                                                         nsd = ques.mayUser(trans, trans.user(), r, Access.write);
3048                                                 }
3049                                                 return nsd;
3050                                         }
3051                                 });
3052                         Result<NsDAO.Data> nsr = ques.deriveNs(trans, userRole.role);
3053                         if(nsr.notOKorIsEmpty()) {
3054                                 return Result.err(nsr);
3055                         }
3056
3057                         switch(fd.status) {
3058                                 case OK:
3059                                         Result<String> rfc = func.createFuture(trans, fd.value, userRole.user+'|'+userRole.ns + '.' + userRole.rname, 
3060                                                         userRole.user, nsr.value, FUTURE_OP.C);
3061                                         if(rfc.isOK()) {
3062                                                 return Result.err(Status.ACC_Future, "UserRole [%s - %s.%s] is saved for future processing",
3063                                                                 userRole.user,
3064                                                                 userRole.ns,
3065                                                                 userRole.rname);
3066                                         } else { 
3067                                                 return Result.err(rfc);
3068                                         }
3069                                 case Status.ACC_Now:
3070                                         return func.addUserRole(trans, userRole);
3071                                 default:
3072                                         return Result.err(fd);
3073                         }
3074                 } finally {
3075                         tt.done();
3076                 }
3077         }
3078         
3079                 /**
3080                  * getUserRolesByRole
3081                  */
3082             @ApiDoc(
3083                     method = GET,
3084                     path = "/authz/userRoles/role/:role",
3085                     params = {"role|string|true"},
3086                     expectedCode = 200,
3087                     errorCodes = {404,406},
3088                     text = { "List all Users that are attached to Role specified in :role",
3089                                 }
3090                    )
3091                 @Override
3092                 public Result<USERROLES> getUserRolesByRole(AuthzTrans trans, String role) {
3093                         final Validator v = new ServiceValidator();
3094                         if(v.nullOrBlank("Role",role).err()) {
3095                                 return Result.err(Status.ERR_BadData,v.errs());
3096                         }
3097                         
3098                         Result<RoleDAO.Data> rrdd;
3099                         rrdd = RoleDAO.Data.decode(trans,ques,role);
3100                         if(rrdd.notOK()) {
3101                                 return Result.err(rrdd);
3102                         }
3103                         // May Requester see result?
3104                         Result<NsDAO.Data> ns = ques.mayUser(trans,trans.user(), rrdd.value,Access.read);
3105                         if (ns.notOK()) {
3106                                 return Result.err(ns);
3107                         }
3108         
3109         //              boolean filter = true;          
3110         //              if (ns.value.isAdmin(trans.user()) || ns.value.isResponsible(trans.user()))
3111         //                      filter = false;
3112                         
3113                         // Get list of roles per user, then add to Roles as we go
3114                         HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3115                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3116                         if(rlurd.isOK()) {
3117                                 for(UserRoleDAO.Data data : rlurd.value) {
3118                                         userSet.add(data);
3119                                 }
3120                         }
3121                         
3122                         @SuppressWarnings("unchecked")
3123                         USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3124                         // Checked for permission
3125                         mapper.userRoles(trans, userSet, users);
3126                         return Result.ok(users);
3127                 }
3128                 /**
3129                  * getUserRolesByRole
3130                  */
3131             @ApiDoc(
3132                     method = GET,
3133                     path = "/authz/userRoles/user/:user",
3134                     params = {"role|string|true"},
3135                     expectedCode = 200,
3136                     errorCodes = {404,406},
3137                     text = { "List all UserRoles for :user",
3138                                 }
3139                    )
3140                 @Override
3141                 public Result<USERROLES> getUserRolesByUser(AuthzTrans trans, String user) {
3142                         final Validator v = new ServiceValidator();
3143                         if(v.nullOrBlank("User",user).err()) {
3144                                 return Result.err(Status.ERR_BadData,v.errs());
3145                         }
3146                         
3147                         // Get list of roles per user, then add to Roles as we go
3148                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, user);
3149                         if(rlurd.notOK()) { 
3150                                 return Result.err(rlurd);
3151                         }
3152                         
3153                         /* Check for
3154                          *   1) is User 
3155                          *   2) is User's Supervisor
3156                          *   3) Has special global access =read permission
3157                          *   
3158                          *   If none of the 3, then filter results to NSs in which Calling User has Ns.access * read
3159                          */
3160                         boolean mustFilter;
3161                         String callingUser = trans.getUserPrincipal().getName();
3162                         NsDAO.Data ndd = new NsDAO.Data();
3163
3164                         if(user.equals(callingUser)) {
3165                                 mustFilter = false;
3166                         } else {
3167                                 Organization org = trans.org();
3168                                 try {
3169                                         Identity orgID = org.getIdentity(trans, user);
3170                                         Identity manager = orgID==null?null:orgID.responsibleTo();
3171                                         if(orgID!=null && (manager!=null && callingUser.equals(manager.fullID()))) {
3172                                                 mustFilter = false;
3173                                         } else if(ques.isGranted(trans, callingUser, ROOT_NS, Question.ACCESS, "*", Access.read.name())) {
3174                                                 mustFilter=false;
3175                                         } else {
3176                                                 mustFilter = true;
3177                                         }
3178                                 } catch (OrganizationException e) {
3179                                         trans.env().log(e);
3180                                         mustFilter = true;
3181                                 }
3182                         }
3183                         
3184                         List<UserRoleDAO.Data> content;
3185                         if(mustFilter) {
3186                                 content = new ArrayList<UserRoleDAO.Data>(rlurd.value.size()); // avoid multi-memory redos
3187                                 
3188                                 for(UserRoleDAO.Data data : rlurd.value) {
3189                                         ndd.name=data.ns;
3190                                         Result<Data> mur = ques.mayUser(trans, callingUser, ndd, Access.read);
3191                                         if(mur.isOK()){
3192                                                 content.add(data);
3193                                         }
3194                                 }
3195                                 
3196                         } else {
3197                                 content = rlurd.value;
3198                         }
3199
3200
3201                         @SuppressWarnings("unchecked")
3202                         USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3203                         // Checked for permission
3204                         mapper.userRoles(trans, content, users);
3205                         return Result.ok(users);
3206                 }
3207
3208             
3209         @ApiDoc( 
3210                         method = PUT,  
3211                         path = "/authz/userRole/user",
3212                         params = {},
3213                         expectedCode = 200,
3214                         errorCodes = {403,404,406}, 
3215                         text = { "Set a User's roles to the roles specified in the UserRoleRequest object.",
3216                                                 "WARNING: Roles supplied will be the ONLY roles attached to this user",
3217                                                 "If no roles are supplied, user's roles are reset."
3218                                    }
3219                         )
3220         @Override
3221         public Result<Void> resetRolesForUser(AuthzTrans trans, REQUEST rreq) {
3222                 Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3223                 final ServiceValidator v = new ServiceValidator();
3224                 if(rurdd.notOKorIsEmpty()) {
3225                         return Result.err(rurdd);
3226                 }
3227                 if (v.user(trans.org(), rurdd.value.user).err()) {
3228                         return Result.err(Status.ERR_BadData,v.errs());
3229                 }
3230
3231                 Set<String> currRoles = new HashSet<String>();
3232                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, rurdd.value.user);
3233                 if(rlurd.isOK()) {
3234                         for(UserRoleDAO.Data data : rlurd.value) {
3235                                 currRoles.add(data.role);
3236                         }
3237                 }
3238                 
3239                 Result<Void> rv = null;
3240                 String[] roles;
3241                 if(rurdd.value.role==null) {
3242                         roles = new String[0];
3243                 } else {
3244                         roles = rurdd.value.role.split(",");
3245                 }
3246                 
3247                 for (String role : roles) {                     
3248                         if (v.role(role).err()) {
3249                                 return Result.err(Status.ERR_BadData,v.errs());
3250                         }
3251                         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3252                         if(rrdd.notOK()) {
3253                                 return Result.err(rrdd);
3254                         }
3255                         
3256                         rurdd.value.role(rrdd.value);
3257                         
3258                         Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rrdd.value,Access.write);
3259                         if (nsd.notOK()) {
3260                                 return Result.err(nsd);
3261                         }
3262                         Result<NsDAO.Data> nsr = ques.deriveNs(trans, role);
3263                         if(nsr.notOKorIsEmpty()) {
3264                                 return Result.err(nsr); 
3265                         }
3266                         
3267                         if(currRoles.contains(role)) {
3268                                 currRoles.remove(role);
3269                         } else {
3270                                 rv = func.addUserRole(trans, rurdd.value);
3271                                 if (rv.notOK()) {
3272                                         return rv;
3273                                 }
3274                         }
3275                 }
3276                 
3277                 for (String role : currRoles) {
3278                         rurdd.value.role(trans,ques,role);
3279                         rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3280                         if(rv.notOK()) {
3281                                 trans.info().log(rurdd.value.user,"/",rurdd.value.role, "expected to be deleted, but does not exist");
3282                                 // return rv; // if it doesn't exist, don't error out
3283                         }
3284
3285                 }
3286         
3287                 return Result.ok();             
3288                 
3289         }
3290         
3291         @ApiDoc( 
3292                         method = PUT,  
3293                         path = "/authz/userRole/role",
3294                         params = {},
3295                         expectedCode = 200,
3296                         errorCodes = {403,404,406}, 
3297                         text = { "Set a Role's users to the users specified in the UserRoleRequest object.",
3298                                         "WARNING: Users supplied will be the ONLY users attached to this role",
3299                                         "If no users are supplied, role's users are reset."
3300                            }
3301                         )
3302         @Override
3303         public Result<Void> resetUsersForRole(AuthzTrans trans, REQUEST rreq) {
3304                 Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3305                 if(rurdd.notOKorIsEmpty()) {
3306                         return Result.err(rurdd);
3307                 }
3308                 final ServiceValidator v = new ServiceValidator();
3309                 if (v.user_role(rurdd.value).err()) {
3310                         return Result.err(Status.ERR_BadData,v.errs());
3311                 }
3312
3313                 RoleDAO.Data rd = RoleDAO.Data.decode(rurdd.value);
3314
3315                 Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rd, Access.write);
3316                 if (nsd.notOK()) {
3317                         return Result.err(nsd);
3318                 }
3319
3320                 Result<NsDAO.Data> nsr = ques.deriveNs(trans, rurdd.value.role);
3321                 if(nsr.notOKorIsEmpty()) {
3322                         return Result.err(nsr); 
3323                 }
3324
3325                 Set<String> currUsers = new HashSet<String>();
3326                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, rurdd.value.role);
3327                 if(rlurd.isOK()) { 
3328                         for(UserRoleDAO.Data data : rlurd.value) {
3329                                 currUsers.add(data.user);
3330                         }
3331                 }
3332         
3333                 // found when connected remotely to DEVL, can't replicate locally
3334                 // inconsistent errors with cmd: role user setTo [nothing]
3335                 // deleteUserRole --> read --> get --> cacheIdx(?)
3336                 // sometimes returns idx for last added user instead of user passed in
3337                 // cache bug? 
3338                 
3339                 
3340                 Result<Void> rv = null;
3341                 String[] users = {};
3342                 if (rurdd.value.user != null) {
3343                     users = rurdd.value.user.split(",");
3344                 }
3345                 
3346                 for (String user : users) {                     
3347                         if (v.user(trans.org(), user).err()) {
3348                                 return Result.err(Status.ERR_BadData,v.errs());
3349                         }
3350                         rurdd.value.user = user;
3351
3352                         if(currUsers.contains(user)) {
3353                                 currUsers.remove(user);
3354                         } else {
3355                                 rv = func.addUserRole(trans, rurdd.value);
3356                                 if (rv.notOK()) { 
3357                                         return rv;
3358                                 }
3359                         }
3360                 }
3361                 
3362                 for (String user : currUsers) {
3363                         rurdd.value.user = user; 
3364                         rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3365                         if(rv.notOK()) {
3366                                 trans.info().log(rurdd.value, "expected to be deleted, but not exists");
3367                                 return rv;
3368                         }
3369                 }       
3370                 
3371                 return Result.ok();                     
3372         }
3373         
3374         @ApiDoc(
3375                 method = GET,
3376                 path = "/authz/userRole/extend/:user/:role",
3377                 params = {      "user|string|true",
3378                                         "role|string|true"
3379                                 },
3380                 expectedCode = 200,
3381                 errorCodes = {403,404,406},
3382                 text = { "Extend the Expiration of this User Role by the amount set by Organization",
3383                                  "Requestor must be allowed to modify the role"
3384                                 }
3385                )
3386         @Override
3387         public Result<Void> extendUserRole(AuthzTrans trans, String user, String role) {
3388                 Organization org = trans.org();
3389                 final ServiceValidator v = new ServiceValidator();
3390                 if(v.user(org, user)
3391                         .role(role)
3392                         .err()) {
3393                         return Result.err(Status.ERR_BadData,v.errs());
3394                 }
3395         
3396                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3397                 if(rrdd.notOK()) {
3398                         return Result.err(rrdd);
3399                 }
3400                 
3401                 Result<NsDAO.Data> rcr = ques.mayUser(trans, trans.user(), rrdd.value, Access.write);
3402                 boolean mayNotChange;
3403                 if((mayNotChange = rcr.notOK()) && !trans.requested(future)) {
3404                         return Result.err(rcr);
3405                 }
3406                 
3407                 Result<List<UserRoleDAO.Data>> rr = ques.userRoleDAO.read(trans, user,role);
3408                 if(rr.notOK()) {
3409                         return Result.err(rr);
3410                 }
3411                 for(UserRoleDAO.Data userRole : rr.value) {
3412                         if(mayNotChange) { // Function exited earlier if !trans.futureRequested
3413                                 FutureDAO.Data fto = new FutureDAO.Data();
3414                                 fto.target=UserRoleDAO.TABLE;
3415                                 fto.memo = "Extend User ["+userRole.user+"] in Role ["+userRole.role+"]";
3416                                 GregorianCalendar now = new GregorianCalendar();
3417                                 fto.start = now.getTime();
3418                                 fto.expires = org.expiration(now, Expiration.Future).getTime();
3419                                 try {
3420                                         fto.construct = userRole.bytify();
3421                                 } catch (IOException e) {
3422                                         trans.error().log(e, "Error while bytifying UserRole for Future");
3423                                         return Result.err(e);
3424                                 }
3425
3426                                 Result<String> rfc = func.createFuture(trans, fto, 
3427                                                 userRole.user+'|'+userRole.role, userRole.user, rcr.value, FUTURE_OP.U);
3428                                 if(rfc.isOK()) {
3429                                         return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3430                                                         userRole.user,
3431                                                         userRole.role);
3432                                 } else {
3433                                         return Result.err(rfc);
3434                                 }
3435                         } else {
3436                                 return func.extendUserRole(trans, userRole, false);
3437                         }
3438                 }
3439                 return Result.err(Result.ERR_NotFound,"This user and role doesn't exist");
3440         }
3441
3442         @ApiDoc( 
3443                         method = DELETE,  
3444                         path = "/authz/userRole/:user/:role",
3445                         params = {      "user|string|true",
3446                                                 "role|string|true"
3447                                         },
3448                         expectedCode = 200,
3449                         errorCodes = {403,404,406}, 
3450                         text = { "Remove Role :role from User :user."
3451                                    }
3452                         )
3453         @Override
3454         public Result<Void> deleteUserRole(AuthzTrans trans, String usr, String role) {
3455                 Validator val = new ServiceValidator();
3456                 if(val.nullOrBlank("User", usr)
3457                       .nullOrBlank("Role", role).err()) {
3458                         return Result.err(Status.ERR_BadData, val.errs());
3459                 }
3460
3461                 boolean mayNotChange;
3462                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3463                 if(rrdd.notOK()) {
3464                         return Result.err(rrdd);
3465                 }
3466                 
3467                 RoleDAO.Data rdd = rrdd.value;
3468                 Result<NsDAO.Data> rns = ques.mayUser(trans, trans.user(), rdd, Access.write);
3469
3470                 // Make sure we don't delete the last owner of valid NS
3471                 if(rns.isOKhasData() && Question.OWNER.equals(rdd.name) && ques.countOwner(trans,rdd.ns)<=1) {
3472                         return Result.err(Status.ERR_Denied,"You may not delete the last Owner of " + rdd.ns );
3473                 }
3474                 
3475                 if(mayNotChange=rns.notOK()) {
3476                         if(!trans.requested(future)) {
3477                                 return Result.err(rns);
3478                         }
3479                 }
3480
3481                 Result<List<UserRoleDAO.Data>> rulr;
3482                 if((rulr=ques.userRoleDAO.read(trans, usr, role)).notOKorIsEmpty()) {
3483                         return Result.err(Status.ERR_UserRoleNotFound, "User [ "+usr+" ] is not "
3484                                         + "Assigned to the Role [ " + role + " ]");
3485                 }
3486
3487                 UserRoleDAO.Data userRole = rulr.value.get(0);
3488                 if(mayNotChange) { // Function exited earlier if !trans.futureRequested
3489                         FutureDAO.Data fto = new FutureDAO.Data();
3490                         fto.target=UserRoleDAO.TABLE;
3491                         fto.memo = "Remove User ["+userRole.user+"] from Role ["+userRole.role+"]";
3492                         GregorianCalendar now = new GregorianCalendar();
3493                         fto.start = now.getTime();
3494                         fto.expires = trans.org().expiration(now, Expiration.Future).getTime();
3495
3496                         Result<String> rfc = func.createFuture(trans, fto, 
3497                                         userRole.user+'|'+userRole.role, userRole.user, rns.value, FUTURE_OP.D);
3498                         if(rfc.isOK()) {
3499                                 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing", 
3500                                                 userRole.user,
3501                                                 userRole.role);
3502                         } else { 
3503                                 return Result.err(rfc);
3504                         }
3505                 } else {
3506                         return ques.userRoleDAO.delete(trans, rulr.value.get(0), false);
3507                 }
3508         }
3509
3510         @ApiDoc( 
3511                         method = GET,  
3512                         path = "/authz/userRole/:user/:role",
3513                         params = {"user|string|true",
3514                                           "role|string|true"},
3515                         expectedCode = 200,
3516                         errorCodes = {403,404,406}, 
3517                         text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3518                                    }
3519                         )
3520         @Override
3521         public Result<USERS> getUserInRole(AuthzTrans trans, String user, String role) {
3522                 final Validator v = new ServiceValidator();
3523                 if(v.role(role).nullOrBlank("User", user).err()) {
3524                         return Result.err(Status.ERR_BadData,v.errs());
3525                 }
3526
3527 //              Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3528 //              if (ns.notOK()) return Result.err(ns);
3529 //              
3530 //              Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3531                 // May calling user see by virtue of the Role
3532                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3533                 if(rrdd.notOK()) {
3534                         return Result.err(rrdd);
3535                 }
3536                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3537                 if(rnd.notOK()) {
3538                         return Result.err(rnd); 
3539                 }
3540                 
3541                 HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3542                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readUserInRole(trans, user, role);
3543                 if(rlurd.isOK()) {
3544                         for(UserRoleDAO.Data data : rlurd.value) {
3545                                 userSet.add(data);
3546                         }
3547                 }
3548                 
3549                 @SuppressWarnings("unchecked")
3550                 USERS users = (USERS) mapper.newInstance(API.USERS);
3551                 mapper.users(trans, userSet, users);
3552                 return Result.ok(users);
3553         }
3554
3555         @ApiDoc( 
3556                         method = GET,  
3557                         path = "/authz/users/role/:role",
3558                         params = {"user|string|true",
3559                                           "role|string|true"},
3560                         expectedCode = 200,
3561                         errorCodes = {403,404,406}, 
3562                         text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3563                                    }
3564                         )
3565         @Override
3566         public Result<USERS> getUsersByRole(AuthzTrans trans, String role) {
3567                 final Validator v = new ServiceValidator();
3568                 if(v.nullOrBlank("Role",role).err()) {
3569                         return Result.err(Status.ERR_BadData,v.errs());
3570                 }
3571
3572 //              Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3573 //              if (ns.notOK()) return Result.err(ns);
3574 //              
3575 //              Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3576                 // May calling user see by virtue of the Role
3577                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3578                 if(rrdd.notOK()) {
3579                         return Result.err(rrdd);
3580                 }
3581                 
3582                 boolean contactOnly = false;
3583                 // Allow the request of any valid user to find the contact of the NS (Owner)
3584                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3585                 if(rnd.notOK()) {
3586                         if(Question.OWNER.equals(rrdd.value.name)) {
3587                                 contactOnly = true;
3588                         } else {
3589                                 return Result.err(rnd);
3590                         }
3591                 }
3592                 
3593                 HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3594                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3595                 if(rlurd.isOK()) { 
3596                         for(UserRoleDAO.Data data : rlurd.value) {
3597                                 if(contactOnly) { //scrub data
3598                                         // Can't change actual object, or will mess up the cache.
3599                                         UserRoleDAO.Data scrub = new UserRoleDAO.Data();
3600                                         scrub.ns = data.ns;
3601                                         scrub.rname = data.rname;
3602                                         scrub.role = data.role;
3603                                         scrub.user = data.user;
3604                                         userSet.add(scrub);
3605                                 } else {
3606                                         userSet.add(data);
3607                                 }
3608                         }
3609                 }
3610                 
3611                 @SuppressWarnings("unchecked")
3612                 USERS users = (USERS) mapper.newInstance(API.USERS);
3613                 mapper.users(trans, userSet, users);
3614                 return Result.ok(users);
3615         }
3616
3617         /**
3618          * getUsersByPermission
3619          */
3620     @ApiDoc(
3621             method = GET,
3622             path = "/authz/users/perm/:type/:instance/:action",
3623             params = {  "type|string|true",
3624                                 "instance|string|true",
3625                                 "action|string|true"
3626                         },
3627             expectedCode = 200,
3628             errorCodes = {404,406},
3629             text = { "List all Users that have Permission specified by :type :instance :action",
3630                         }
3631            )
3632         @Override
3633         public Result<USERS> getUsersByPermission(AuthzTrans trans, String type, String instance, String action) {
3634                 final Validator v = new ServiceValidator();
3635                 if(v.nullOrBlank("Type",type)
3636                         .nullOrBlank("Instance",instance)
3637                         .nullOrBlank("Action",action)                   
3638                         .err()) {
3639                         return Result.err(Status.ERR_BadData,v.errs());
3640                 }
3641
3642                 Result<NsSplit> nss = ques.deriveNsSplit(trans, type);
3643                 if(nss.notOK()) {
3644                         return Result.err(nss);
3645                 }
3646                 
3647                 Result<List<NsDAO.Data>> nsd = ques.nsDAO.read(trans, nss.value.ns);
3648                 if (nsd.notOK()) {
3649                         return Result.err(nsd);
3650                 }
3651                 
3652                 boolean allInstance = ASTERIX.equals(instance);
3653                 boolean allAction = ASTERIX.equals(action);
3654                 // Get list of roles per Permission, 
3655                 // Then loop through Roles to get Users
3656                 // Note: Use Sets to avoid processing or responding with Duplicates
3657                 Set<String> roleUsed = new HashSet<String>();
3658                 Set<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3659                 
3660                 if(!nss.isEmpty()) {
3661                         Result<List<PermDAO.Data>> rlp = ques.permDAO.readByType(trans, nss.value.ns, nss.value.name);
3662                         if(rlp.isOKhasData()) {
3663                                 for(PermDAO.Data pd : rlp.value) {
3664                                         if((allInstance || pd.instance.equals(instance)) && 
3665                                                         (allAction || pd.action.equals(action))) {
3666                                                 if(ques.mayUser(trans, trans.user(),pd,Access.read).isOK()) {
3667                                                         for(String role : pd.roles) {
3668                                                                 if(!roleUsed.contains(role)) { // avoid evaluating Role many times
3669                                                                         roleUsed.add(role);
3670                                                                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role.replace('|', '.'));
3671                                                                         if(rlurd.isOKhasData()) {
3672                                                                             for(UserRoleDAO.Data urd : rlurd.value) {
3673                                                                                 userSet.add(urd);
3674                                                                             }
3675                                                                         }
3676                                                                 }
3677                                                         }
3678                                                 }
3679                                         }
3680                                 }
3681                         }
3682                 }
3683                 @SuppressWarnings("unchecked")
3684                 USERS users = (USERS) mapper.newInstance(API.USERS);
3685                 mapper.users(trans, userSet, users);
3686                 return Result.ok(users);
3687         }
3688
3689     /***********************************
3690  * HISTORY 
3691  ***********************************/   
3692         @Override
3693         public Result<HISTORY> getHistoryByUser(final AuthzTrans trans, String user, final int[] yyyymm, final int sort) {      
3694                 final Validator v = new ServiceValidator();
3695                 if(v.nullOrBlank("User",user).err()) {
3696                         return Result.err(Status.ERR_BadData,v.errs());
3697                 }
3698
3699                 Result<NsDAO.Data> rnd;
3700                 // Users may look at their own data
3701                  if(trans.user().equals(user)) {
3702                                 // Users may look at their own data
3703                  } else {
3704                         int at = user.indexOf('@');
3705                         if(at>=0 && trans.org().getRealm().equals(user.substring(at+1))) {
3706                                 NsDAO.Data nsd  = new NsDAO.Data();
3707                                 nsd.name = Question.domain2ns(user);
3708                                 rnd = ques.mayUser(trans, trans.user(), nsd, Access.read);
3709                                 if(rnd.notOK()) {
3710                                         return Result.err(rnd);
3711                                 }
3712                         } else {
3713                                 rnd = ques.validNSOfDomain(trans, user);
3714                                 if(rnd.notOK()) {
3715                                         return Result.err(rnd);
3716                                 }
3717
3718                                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3719                                 if(rnd.notOK()) {
3720                                         return Result.err(rnd);
3721                                 }
3722                         }
3723                  }
3724                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readByUser(trans, user, yyyymm);
3725                 if(resp.notOK()) {
3726                         return Result.err(resp);
3727                 }
3728                 return mapper.history(trans, resp.value,sort);
3729         }
3730
3731         @Override
3732         public Result<HISTORY> getHistoryByRole(AuthzTrans trans, String role, int[] yyyymm, final int sort) {
3733                 final Validator v = new ServiceValidator();
3734                 if(v.nullOrBlank("Role",role).err()) {
3735                         return Result.err(Status.ERR_BadData,v.errs());
3736                 }
3737
3738                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3739                 if(rrdd.notOK()) {
3740                         return Result.err(rrdd);
3741                 }
3742                 
3743                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
3744                 if(rnd.notOK()) {
3745                         return Result.err(rnd);
3746                 }
3747                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, role, "role", yyyymm); 
3748                 if(resp.notOK()) {
3749                         return Result.err(resp);
3750                 }
3751                 return mapper.history(trans, resp.value,sort);
3752         }
3753
3754         @Override
3755         public Result<HISTORY> getHistoryByPerm(AuthzTrans trans, String type, int[] yyyymm, final int sort) {
3756                 final Validator v = new ServiceValidator();
3757                 if(v.nullOrBlank("Type",type)
3758                         .err()) {
3759                         return Result.err(Status.ERR_BadData,v.errs());
3760                 }
3761
3762                 // May user see Namespace of Permission (since it's only one piece... we can't check for "is permission part of")
3763                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,type);
3764                 if(rnd.notOK()) {
3765                         return Result.err(rnd);
3766                 }
3767                 
3768                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3769                 if(rnd.notOK()) {
3770                         return Result.err(rnd); 
3771                 }
3772                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, type, "perm", yyyymm);
3773                 if(resp.notOK()) {
3774                         return Result.err(resp);
3775                 }
3776                 return mapper.history(trans, resp.value,sort);
3777         }
3778
3779         @Override
3780         public Result<HISTORY> getHistoryByNS(AuthzTrans trans, String ns, int[] yyyymm, final int sort) {
3781                 final Validator v = new ServiceValidator();
3782                 if(v.nullOrBlank("NS",ns)
3783                         .err()) { 
3784                         return Result.err(Status.ERR_BadData,v.errs());
3785                 }
3786
3787                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
3788                 if(rnd.notOK()) {
3789                         return Result.err(rnd);
3790                 }
3791                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3792                 if(rnd.notOK()) {
3793                         return Result.err(rnd); 
3794                 }
3795
3796                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, ns, "ns", yyyymm);
3797                 if(resp.notOK()) {
3798                         return Result.err(resp);
3799                 }
3800                 return mapper.history(trans, resp.value,sort);
3801         }
3802
3803 /***********************************
3804  * DELEGATE 
3805  ***********************************/
3806         @Override
3807         public Result<Void> createDelegate(final AuthzTrans trans, REQUEST base) {
3808                 return createOrUpdateDelegate(trans, base, Question.Access.create);
3809         }
3810
3811         @Override
3812         public Result<Void> updateDelegate(AuthzTrans trans, REQUEST base) {
3813                 return createOrUpdateDelegate(trans, base, Question.Access.write);
3814         }
3815
3816
3817         private Result<Void> createOrUpdateDelegate(final AuthzTrans trans, REQUEST base, final Access access) {
3818                 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3819                 final ServiceValidator v = new ServiceValidator();
3820                 if(v.delegate(trans.org(),rd).err()) { 
3821                         return Result.err(Status.ERR_BadData,v.errs());
3822                 }
3823
3824                 final DelegateDAO.Data dd = rd.value;
3825                 
3826                 Result<List<DelegateDAO.Data>> ddr = ques.delegateDAO.read(trans, dd);
3827                 if(access==Access.create && ddr.isOKhasData()) {
3828                         return Result.err(Status.ERR_ConflictAlreadyExists, "[%s] already delegates to [%s]", dd.user, ddr.value.get(0).delegate);
3829                 } else if(access!=Access.create && ddr.notOKorIsEmpty()) { 
3830                         return Result.err(Status.ERR_NotFound, "[%s] does not have a Delegate Record to [%s].",dd.user,access.name());
3831                 }
3832                 Result<Void> rv = ques.mayUser(trans, dd, access);
3833                 if(rv.notOK()) {
3834                         return rv;
3835                 }
3836                 
3837                 Result<FutureDAO.Data> fd = mapper.future(trans,DelegateDAO.TABLE,base, dd, false, 
3838                         new Mapper.Memo() {
3839                                 @Override
3840                                 public String get() {
3841                                         StringBuilder sb = new StringBuilder();
3842                                         sb.append(access.name());
3843                                         sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
3844                                         sb.append("Delegate ");
3845                                         sb.append(access==Access.create?"[":"to [");
3846                                         sb.append(rd.value.delegate);
3847                                         sb.append("] for [");
3848                                         sb.append(rd.value.user);
3849                                         sb.append(']');
3850                                         return sb.toString();
3851                                 }
3852                         },
3853                         new MayChange() {
3854                                 @Override
3855                                 public Result<?> mayChange() {
3856                                         return Result.ok(); // Validate in code above
3857                                 }
3858                         });
3859                 
3860                 switch(fd.status) {
3861                         case OK:
3862                                 Result<String> rfc = func.createFuture(trans, fd.value, 
3863                                                 dd.user, trans.user(),null, access==Access.create?FUTURE_OP.C:FUTURE_OP.U);
3864                                 if(rfc.isOK()) { 
3865                                         return Result.err(Status.ACC_Future, "Delegate for [%s]",
3866                                                         dd.user);
3867                                 } else { 
3868                                         return Result.err(rfc);
3869                                 }
3870                         case Status.ACC_Now:
3871                                 if(access==Access.create) {
3872                                         Result<DelegateDAO.Data> rdr = ques.delegateDAO.create(trans, dd);
3873                                         if(rdr.isOK()) {
3874                                                 return Result.ok();
3875                                         } else {
3876                                                 return Result.err(rdr);
3877                                         }
3878                                 } else {
3879                                         return ques.delegateDAO.update(trans, dd);
3880                                 }
3881                         default:
3882                                 return Result.err(fd);
3883                 }
3884         }
3885
3886         @Override
3887         public Result<Void> deleteDelegate(AuthzTrans trans, REQUEST base) {
3888                 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3889                 final Validator v = new ServiceValidator();
3890                 if(v.notOK(rd).nullOrBlank("User", rd.value.user).err()) {
3891                         return Result.err(Status.ERR_BadData,v.errs());
3892                 }
3893                 
3894                 Result<List<DelegateDAO.Data>> ddl;
3895                 if((ddl=ques.delegateDAO.read(trans, rd.value)).notOKorIsEmpty()) {
3896                         return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3897                 }
3898                 final DelegateDAO.Data dd = ddl.value.get(0);
3899                 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3900                 if(rv.notOK()) {
3901                         return rv;
3902                 }
3903                 
3904                 return ques.delegateDAO.delete(trans, dd, false);
3905         }
3906
3907         @Override
3908         public Result<Void> deleteDelegate(AuthzTrans trans, String userName) {
3909                 DelegateDAO.Data dd = new DelegateDAO.Data();
3910                 final Validator v = new ServiceValidator();
3911                 if(v.nullOrBlank("User", userName).err()) {
3912                         return Result.err(Status.ERR_BadData,v.errs());
3913                 }
3914                 dd.user = userName;
3915                 Result<List<DelegateDAO.Data>> ddl;
3916                 if((ddl=ques.delegateDAO.read(trans, dd)).notOKorIsEmpty()) {
3917                         return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3918                 }
3919                 dd = ddl.value.get(0);
3920                 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3921                 if(rv.notOK()) {
3922                         return rv;
3923                 }
3924                 
3925                 return ques.delegateDAO.delete(trans, dd, false);
3926         }
3927         
3928         @Override
3929         public Result<DELGS> getDelegatesByUser(AuthzTrans trans, String user) {
3930                 final Validator v = new ServiceValidator();
3931                 if(v.nullOrBlank("User", user).err()) {
3932                         return Result.err(Status.ERR_BadData,v.errs());
3933                 }
3934
3935                 DelegateDAO.Data ddd = new DelegateDAO.Data();
3936                 ddd.user = user;
3937                 ddd.delegate = null;
3938                 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3939                 if(rv.notOK()) {
3940                         return Result.err(rv);
3941                 }
3942                 
3943                 TimeTaken tt = trans.start("Get delegates for a user", Env.SUB);
3944
3945                 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.read(trans, user);
3946                 try {
3947                         if (dbDelgs.isOKhasData()) {
3948                                 return mapper.delegate(dbDelgs.value);
3949                         } else {
3950                                 return Result.err(Status.ERR_DelegateNotFound,"No Delegate found for [%s]",user);
3951                         }
3952                 } finally {
3953                         tt.done();
3954                 }               
3955         }
3956
3957         @Override
3958         public Result<DELGS> getDelegatesByDelegate(AuthzTrans trans, String delegate) {
3959                 final Validator v = new ServiceValidator();
3960                 if(v.nullOrBlank("Delegate", delegate).err()) {
3961                         return Result.err(Status.ERR_BadData,v.errs());
3962                 }
3963
3964                 DelegateDAO.Data ddd = new DelegateDAO.Data();
3965                 ddd.user = delegate;
3966                 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3967                 if(rv.notOK()) {
3968                         return Result.err(rv);
3969                 }
3970
3971                 TimeTaken tt = trans.start("Get users for a delegate", Env.SUB);
3972
3973                 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.readByDelegate(trans, delegate);
3974                 try {
3975                         if (dbDelgs.isOKhasData()) {
3976                                 return mapper.delegate(dbDelgs.value);
3977                         } else {
3978                                 return Result.err(Status.ERR_DelegateNotFound,"Delegate [%s] is not delegating for anyone.",delegate);
3979                         }
3980                 } finally {
3981                         tt.done();
3982                 }               
3983         }
3984
3985 /***********************************
3986  * APPROVAL 
3987  ***********************************/
3988         private static final String APPR_FMT = "actor=%s, action=%s, operation=\"%s\", requestor=%s, delegator=%s";
3989         @Override
3990         public Result<Void> updateApproval(AuthzTrans trans, APPROVALS approvals) {
3991                 Result<List<ApprovalDAO.Data>> rlad = mapper.approvals(approvals);
3992                 if(rlad.notOK()) {
3993                         return Result.err(rlad);
3994                 }
3995                 int numApprs = rlad.value.size();
3996                 if(numApprs<1) {
3997                         return Result.err(Status.ERR_NoApprovals,"No Approvals sent for Updating");
3998                 }
3999                 int numProcessed = 0;
4000                 String user = trans.user();
4001                 
4002                 Result<List<ApprovalDAO.Data>> curr;
4003                 Lookup<List<ApprovalDAO.Data>> apprByTicket=null;
4004                 for(ApprovalDAO.Data updt : rlad.value) {
4005                         if(updt.ticket!=null) {
4006                                 curr = ques.approvalDAO.readByTicket(trans, updt.ticket);
4007                                 if(curr.isOKhasData()) {
4008                                         final List<ApprovalDAO.Data> add = curr.value;
4009                                         apprByTicket = new Lookup<List<ApprovalDAO.Data>>() { // Store a Pre-Lookup
4010                                                 @Override
4011                                                 public List<ApprovalDAO.Data> get(AuthzTrans trans, Object ... noop) {
4012                                                         return add;
4013                                                 }
4014                                         };
4015                                 }
4016                         } else if(updt.id!=null) {
4017                                 curr = ques.approvalDAO.read(trans, updt);
4018                         } else if(updt.approver!=null) {
4019                                 curr = ques.approvalDAO.readByApprover(trans, updt.approver);
4020                         } else {
4021                                 return Result.err(Status.ERR_BadData,"Approvals need ID, Ticket or Approval data to update");
4022                         }
4023
4024                         if(curr.isOKhasData()) {
4025                         Map<String, Result<List<DelegateDAO.Data>>> delegateCache = new HashMap<String, Result<List<DelegateDAO.Data>>>();
4026                         Map<UUID, FutureDAO.Data> futureCache = new HashMap<UUID, FutureDAO.Data>();
4027                         FutureDAO.Data hasDeleted = new FutureDAO.Data();
4028                         
4029                             for(ApprovalDAO.Data cd : curr.value) {
4030                                 if("pending".equals(cd.status)) {
4031                                                 // Check for right record.  Need ID, or (Ticket&Trans.User==Appr)
4032                                         // If Default ID
4033                                         boolean delegatedAction = ques.isDelegated(trans, user, cd.approver, delegateCache);
4034                                         String delegator = cd.approver;
4035                                         if(updt.id!=null || 
4036                                                 (updt.ticket!=null && user.equals(cd.approver)) ||
4037                                                 (updt.ticket!=null && delegatedAction)) {
4038                                                 if(updt.ticket.equals(cd.ticket)) {
4039                                                         Changed ch = new Changed();
4040                                                         cd.id = ch.changed(cd.id,updt.id);
4041 //                                                      cd.ticket = changed(cd.ticket,updt.ticket);
4042                                                         cd.user = ch.changed(cd.user,updt.user);
4043                                                         cd.approver = ch.changed(cd.approver,updt.approver);
4044                                                         cd.type = ch.changed(cd.type,updt.type);
4045                                                         cd.status = ch.changed(cd.status,updt.status);
4046                                                         cd.memo = ch.changed(cd.memo,updt.memo);
4047                                                         cd.operation = ch.changed(cd.operation,updt.operation);
4048                                                         cd.updated = ch.changed(cd.updated,updt.updated==null?new Date():updt.updated);
4049                                                         if(updt.status.equals("denied")) {
4050                                                                 cd.last_notified = null;
4051                                                         }
4052                                                         if(cd.ticket!=null) {
4053                                                                 FutureDAO.Data fdd = futureCache.get(cd.ticket);
4054                                                                 if(fdd==null) { // haven't processed ticket yet
4055                                                                         Result<FutureDAO.Data> rfdd = ques.futureDAO.readPrimKey(trans, cd.ticket);
4056                                                                         if(rfdd.isOK()) {
4057                                                                                 fdd = rfdd.value; // null is ok
4058                                                                         } else {
4059                                                                                 fdd = hasDeleted;
4060                                                                         }
4061                                                                         futureCache.put(cd.ticket, fdd); // processed this Ticket... don't do others on this ticket
4062                                                                 }
4063                                                                 if(fdd==hasDeleted) { // YES, by Object
4064                                                                         cd.ticket = null;
4065                                                                         cd.status = "ticketDeleted";
4066                                                                         ch.hasChanged(true);
4067                                                                 } else {
4068                                                                         FUTURE_OP fop = FUTURE_OP.toFO(cd.operation);
4069                                                                         if(fop==null) {
4070                                                                                 trans.info().printf("Approval Status %s is not actionable",cd.status);
4071                                                                         } else if(apprByTicket!=null) {
4072                                                                                 Result<OP_STATUS> rv = func.performFutureOp(trans, fop, fdd, apprByTicket,func.urDBLookup);
4073                                                                                 if (rv.isOK()) {
4074                                                                                         switch(rv.value) {
4075                                                                                                 case E:
4076                                                                                                         if (delegatedAction) {
4077                                                                                                                 trans.audit().printf(APPR_FMT,user,updt.status,cd.memo,cd.user,delegator);
4078                                                                                                         }
4079                                                                                                         futureCache.put(cd.ticket, hasDeleted);
4080                                                                                                         break;
4081                                                                                                 case D:
4082                                                                                                 case L:
4083                                                                                                         ch.hasChanged(true);
4084                                                                                                         trans.audit().printf(APPR_FMT,user,rv.value.desc(),cd.memo,cd.user,delegator);
4085                                                                                                         futureCache.put(cd.ticket, hasDeleted);
4086                                                                                                         break;
4087                                                                                                 default:
4088                                                                                         }
4089                                                                                 } else {
4090                                                                                         trans.info().log(rv.toString());
4091                                                                                 }
4092                                                                         }
4093
4094                                                                 }
4095                                                                 ++numProcessed;
4096                                                         }
4097                                                         if(ch.hasChanged()) {
4098                                                                 ques.approvalDAO.update(trans, cd, true);
4099                                                         }
4100                                                 }
4101                                         }
4102                                     }
4103                             }
4104                         }
4105                 }
4106
4107                 if(numApprs==numProcessed) {
4108                         return Result.ok();
4109                 }
4110                 return Result.err(Status.ERR_ActionNotCompleted,numProcessed + " out of " + numApprs + " completed");
4111
4112         }
4113         
4114         private static class Changed {
4115                 private boolean hasChanged = false;
4116
4117                 public<T> T changed(T src, T proposed) {
4118                         if(proposed==null || (src!=null && src.equals(proposed))) {
4119                             return src;
4120                         }
4121                         hasChanged=true;
4122                         return proposed;
4123                 }
4124
4125                 public void hasChanged(boolean b) {
4126                         hasChanged=b;
4127                 }
4128
4129                 public boolean hasChanged() {
4130                         return hasChanged;
4131                 }
4132         }
4133
4134         @Override
4135         public Result<APPROVALS> getApprovalsByUser(AuthzTrans trans, String user) {
4136                 final Validator v = new ServiceValidator();
4137                 if(v.nullOrBlank("User", user).err()) { 
4138                         return Result.err(Status.ERR_BadData,v.errs());
4139                 }
4140
4141                 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByUser(trans, user);
4142                 if(rapd.isOK()) {
4143                         return mapper.approvals(rapd.value);
4144                 } else {
4145                         return Result.err(rapd);
4146                 }
4147 }
4148
4149         @Override
4150         public Result<APPROVALS> getApprovalsByTicket(AuthzTrans trans, String ticket) {
4151                 final Validator v = new ServiceValidator();
4152                 if(v.nullOrBlank("Ticket", ticket).err()) { 
4153                         return Result.err(Status.ERR_BadData,v.errs());
4154                 }
4155                 UUID uuid;
4156                 try {
4157                         uuid = UUID.fromString(ticket);
4158                 } catch (IllegalArgumentException e) {
4159                         return Result.err(Status.ERR_BadData,e.getMessage());
4160                 }
4161         
4162                 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByTicket(trans, uuid);
4163                 if(rapd.isOK()) {
4164                         return mapper.approvals(rapd.value);
4165                 } else {
4166                         return Result.err(rapd);
4167                 }
4168         }
4169         
4170         @Override
4171         public Result<APPROVALS> getApprovalsByApprover(AuthzTrans trans, String approver) {
4172                 final Validator v = new ServiceValidator();
4173                 if(v.nullOrBlank("Approver", approver).err()) {
4174                         return Result.err(Status.ERR_BadData,v.errs());
4175                 }
4176                 
4177                 List<ApprovalDAO.Data> listRapds = new ArrayList<ApprovalDAO.Data>();
4178                 
4179                 Result<List<ApprovalDAO.Data>> myRapd = ques.approvalDAO.readByApprover(trans, approver);
4180                 if(myRapd.notOK()) {
4181                         return Result.err(myRapd);
4182                 }
4183                 
4184                 listRapds.addAll(myRapd.value);
4185                 
4186                 Result<List<DelegateDAO.Data>> delegatedFor = ques.delegateDAO.readByDelegate(trans, approver);
4187                 if (delegatedFor.isOK()) {
4188                         for (DelegateDAO.Data dd : delegatedFor.value) {
4189                                 if (dd.expires.after(new Date())) {
4190                                         String delegator = dd.user;
4191                                         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByApprover(trans, delegator);
4192                                         if (rapd.isOK()) {
4193                                                 for (ApprovalDAO.Data d : rapd.value) { 
4194                                                         if (!d.user.equals(trans.user())) {
4195                                                                 listRapds.add(d);
4196                                                         }
4197                                                 }
4198                                         }
4199                                 }
4200                         }
4201                 }
4202                 
4203                 return mapper.approvals(listRapds);
4204         }
4205         
4206         /* (non-Javadoc)
4207          * @see org.onap.aaf.auth.service.AuthzService#clearCache(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
4208          */
4209         @Override
4210         public Result<Void> cacheClear(AuthzTrans trans, String cname) {
4211                 if(ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4212                         return ques.clearCache(trans,cname);
4213                 }
4214                 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4215                                 trans.user(),ROOT_NS,CACHE,cname);
4216         }
4217
4218         /* (non-Javadoc)
4219          * @see org.onap.aaf.auth.service.AuthzService#cacheClear(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.Integer)
4220          */
4221         @Override
4222         public Result<Void> cacheClear(AuthzTrans trans, String cname, int[] segment) {
4223                 if(ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4224                         Result<Void> v=null;
4225                         for(int i: segment) {
4226                                 v=ques.cacheClear(trans,cname,i);
4227                         }
4228                         if(v!=null) {
4229                                 return v;
4230                         }
4231                 }
4232                 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4233                                 trans.user(),ROOT_NS,CACHE,cname);
4234         }
4235
4236         /* (non-Javadoc)
4237          * @see org.onap.aaf.auth.service.AuthzService#dbReset(org.onap.aaf.auth.env.test.AuthzTrans)
4238          */
4239         @Override
4240         public void dbReset(AuthzTrans trans) {
4241                 ques.historyDAO.reportPerhapsReset(trans, null);
4242         }
4243
4244 }
4245