519721cefd4be5295ada659ac2718e350fc1a3b3
[aaf/authz.git] / auth / auth-service / src / main / java / org / onap / aaf / auth / service / AuthzCassServiceImpl.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.service;
23
24 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.force;
25 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.future;
26 import static org.onap.aaf.auth.layer.Result.OK;
27 import static org.onap.aaf.auth.rserv.HttpMethods.DELETE;
28 import static org.onap.aaf.auth.rserv.HttpMethods.GET;
29 import static org.onap.aaf.auth.rserv.HttpMethods.POST;
30 import static org.onap.aaf.auth.rserv.HttpMethods.PUT;
31
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.Date;
38 import java.util.GregorianCalendar;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.TreeMap;
45 import java.util.UUID;
46
47 import javax.servlet.http.HttpServletRequest;
48
49 import org.onap.aaf.auth.common.Define;
50 import org.onap.aaf.auth.dao.DAOException;
51 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
52 import org.onap.aaf.auth.dao.cass.CertDAO;
53 import org.onap.aaf.auth.dao.cass.CredDAO;
54 import org.onap.aaf.auth.dao.cass.DelegateDAO;
55 import org.onap.aaf.auth.dao.cass.FutureDAO;
56 import org.onap.aaf.auth.dao.cass.HistoryDAO;
57 import org.onap.aaf.auth.dao.cass.Namespace;
58 import org.onap.aaf.auth.dao.cass.NsDAO;
59 import org.onap.aaf.auth.dao.cass.NsDAO.Data;
60 import org.onap.aaf.auth.dao.cass.NsSplit;
61 import org.onap.aaf.auth.dao.cass.NsType;
62 import org.onap.aaf.auth.dao.cass.PermDAO;
63 import org.onap.aaf.auth.dao.cass.RoleDAO;
64 import org.onap.aaf.auth.dao.cass.Status;
65 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
66 import org.onap.aaf.auth.dao.hl.CassExecutor;
67 import org.onap.aaf.auth.dao.hl.Function;
68 import org.onap.aaf.auth.dao.hl.Function.FUTURE_OP;
69 import org.onap.aaf.auth.dao.hl.Function.Lookup;
70 import org.onap.aaf.auth.dao.hl.Function.OP_STATUS;
71 import org.onap.aaf.auth.dao.hl.Question;
72 import org.onap.aaf.auth.dao.hl.Question.Access;
73 import org.onap.aaf.auth.env.AuthzTrans;
74 import org.onap.aaf.auth.layer.Result;
75 import org.onap.aaf.auth.org.Executor;
76 import org.onap.aaf.auth.org.Organization;
77 import org.onap.aaf.auth.org.Organization.Expiration;
78 import org.onap.aaf.auth.org.Organization.Identity;
79 import org.onap.aaf.auth.org.Organization.Policy;
80 import org.onap.aaf.auth.org.OrganizationException;
81 import org.onap.aaf.auth.rserv.doc.ApiDoc;
82 import org.onap.aaf.auth.service.mapper.Mapper;
83 import org.onap.aaf.auth.service.mapper.Mapper.API;
84 import org.onap.aaf.auth.service.validation.ServiceValidator;
85 import org.onap.aaf.auth.validation.Validator;
86 import org.onap.aaf.cadi.principal.BasicPrincipal;
87 import org.onap.aaf.misc.env.Env;
88 import org.onap.aaf.misc.env.TimeTaken;
89 import org.onap.aaf.misc.env.util.Chrono;
90 import org.onap.aaf.misc.env.util.Split;
91
92 import aaf.v2_0.CredRequest;
93
94 /**
95  * AuthzCassServiceImpl implements AuthzCassService for 
96  * 
97  * @author Jonathan
98  *
99  * @param <NSS>
100  * @param <PERMS>
101  * @param <PERMKEY>
102  * @param <ROLES>
103  * @param <USERS>
104  * @param <DELGS>
105  * @param <REQUEST>
106  * @param <HISTORY>
107  * @param <ERR>
108  * @param <APPROVALS>
109  */
110 public class AuthzCassServiceImpl       <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS>
111         implements AuthzService                 <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> {
112         
113         private Mapper                                  <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper;
114         @Override
115         public Mapper                                   <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper() {return mapper;}
116         
117         private static final String ASTERIX = "*";
118         private static final String CACHE = "cache";
119         private static final String ROOT_NS = Define.ROOT_NS();
120         private static final String ROOT_COMPANY = Define.ROOT_COMPANY();
121
122         private final Question ques;
123         private final Function func;
124         
125         public AuthzCassServiceImpl(AuthzTrans trans, Mapper<NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper,Question question) {
126                 this.ques = question;
127                 func = new Function(trans, question);
128                 this.mapper = mapper;
129                 
130         }
131
132 /***********************************
133  * NAMESPACE 
134  ***********************************/
135         /**
136          * createNS
137          * @throws DAOException 
138          * @see org.onap.aaf.auth.service.AuthzService#createNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
139          */
140         @ApiDoc( 
141                         method = POST,  
142                         path = "/authz/ns",
143                         params = {},
144                         expectedCode = 201,
145                         errorCodes = { 403,404,406,409 }, 
146                         text = { "Namespace consists of: ",
147                                         "<ul><li>name - What you want to call this Namespace</li>",
148                                         "<li>responsible(s) - Person(s) who receive Notifications and approves Requests ",
149                                         "regarding this Namespace. Companies have Policies as to who may take on ",
150                                         "this Responsibility. Separate multiple identities with commas</li>",
151                                         "<li>admin(s) - Person(s) who are allowed to make changes on the namespace, ",
152                                         "including creating Roles, Permissions and Credentials. Separate multiple ",
153                                         "identities with commas</li></ul>",
154                                         "Note: Namespaces are dot-delimited (i.e. com.myCompany.myApp) and must be ",
155                                         "created with parent credentials (i.e. To create com.myCompany.myApp, you must ",
156                                         "be an admin of com.myCompany or com"
157                                         }
158                         )
159         @Override
160         public Result<Void> createNS(final AuthzTrans trans, REQUEST from, NsType type) {
161                 final Result<Namespace> rnamespace = mapper.ns(trans, from);
162                 final ServiceValidator v = new ServiceValidator();
163                 if(v.ns(rnamespace).err()) { 
164                         return Result.err(Status.ERR_BadData,v.errs());
165                 }
166                 final Namespace namespace = rnamespace.value;
167                 final Result<NsDAO.Data> parentNs = ques.deriveNs(trans,namespace.name);
168                 if(parentNs.notOK()) {
169                         return Result.err(parentNs);
170                 }
171                 
172                 if(namespace.name.lastIndexOf('.')<0) { // Root Namespace... Function will check if allowed
173                         return func.createNS(trans, namespace, false);
174                 }
175                 
176                 Result<FutureDAO.Data> fd = mapper.future(trans, NsDAO.TABLE,from,namespace,true, 
177                                 new Mapper.Memo() {
178                                         @Override
179                                         public String get() {
180                                                 return "Create Namespace [" + namespace.name + ']';
181                                         }
182                                 },
183                                 new MayChange() {
184                                         private Result<NsDAO.Data> rnd;
185                                         @Override
186                                         public Result<?> mayChange() {
187                                                 if(rnd==null) {
188                                                         rnd = ques.mayUser(trans, trans.user(), parentNs.value,Access.write);
189                                                 }
190                                                 return rnd;
191                                         }
192                                 });
193                         switch(fd.status) {
194                                 case OK:
195                                         Result<String> rfc = func.createFuture(trans, fd.value, namespace.name, trans.user(),parentNs.value, FUTURE_OP.C);
196                                         if(rfc.isOK()) {
197                                                 return Result.err(Status.ACC_Future, "NS [%s] is saved for future processing",namespace.name);
198                                         } else { 
199                                                 return Result.err(rfc);
200                                         }
201                                 case Status.ACC_Now:
202                                         return func.createNS(trans, namespace, false);
203                                 default:
204                                         return Result.err(fd);
205                         }
206         }
207         
208         @ApiDoc(
209                         method = POST,  
210                         path = "/authz/ns/:ns/admin/:id",
211                         params = {      "ns|string|true",
212                                                 "id|string|true" 
213                                         },
214                         expectedCode = 201,
215                         errorCodes = { 403,404,406,409 }, 
216                         text = {        "Add an Identity :id to the list of Admins for the Namespace :ns", 
217                                                 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
218                         )
219         @Override
220         public Result<Void> addAdminNS(AuthzTrans trans, String ns, String id) {
221                 return func.addUserRole(trans, id, ns,Question.ADMIN);
222         }
223
224         @ApiDoc(
225                         method = DELETE,  
226                         path = "/authz/ns/:ns/admin/:id",
227                         params = {      "ns|string|true",
228                                                 "id|string|true" 
229                                         },
230                         expectedCode = 200,
231                         errorCodes = { 403,404 }, 
232                         text = {        "Remove an Identity :id from the list of Admins for the Namespace :ns",
233                                                 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
234                         )
235         @Override
236         public Result<Void> delAdminNS(AuthzTrans trans, String ns, String id) {
237                 return func.delAdmin(trans,ns,id);
238         }
239
240         @ApiDoc(
241                         method = POST,  
242                         path = "/authz/ns/:ns/responsible/:id",
243                         params = {      "ns|string|true",
244                                                 "id|string|true" 
245                                         },
246                         expectedCode = 201,
247                         errorCodes = { 403,404,406,409 }, 
248                         text = {        "Add an Identity :id to the list of Responsibles for the Namespace :ns",
249                                                 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
250                         )
251         @Override
252         public Result<Void> addResponsibleNS(AuthzTrans trans, String ns, String id) {
253                 return func.addUserRole(trans,id,ns,Question.OWNER);
254         }
255
256         @ApiDoc(
257                         method = DELETE,  
258                         path = "/authz/ns/:ns/responsible/:id",
259                         params = {      "ns|string|true",
260                                                 "id|string|true" 
261                                         },
262                         expectedCode = 200,
263                         errorCodes = { 403,404 }, 
264                         text = {        "Remove an Identity :id to the list of Responsibles for the Namespace :ns",
265                                                 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)",
266                                                 "Note: A namespace must have at least 1 responsible party"
267                                         }
268                         )
269         @Override
270         public Result<Void> delResponsibleNS(AuthzTrans trans, String ns, String id) {
271                 return func.delOwner(trans,ns,id);
272         }
273
274         /* (non-Javadoc)
275          * @see org.onap.aaf.auth.service.AuthzService#applyModel(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
276          */
277         @ApiDoc(
278                         method = POST,  
279                         path = "/authz/ns/:ns/attrib/:key/:value",
280                         params = {      "ns|string|true",
281                                                 "key|string|true",
282                                                 "value|string|true"},
283                         expectedCode = 201,
284                         errorCodes = { 403,404,406,409 },  
285                         text = {        
286                                 "Create an attribute in the Namespace",
287                                 "You must be given direct permission for key by AAF"
288                                 }
289                         )
290         @Override
291         public Result<Void> createNsAttrib(AuthzTrans trans, String ns, String key, String value) {
292                 TimeTaken tt = trans.start("Create NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
293                 try {
294                         // Check inputs
295                         final Validator v = new ServiceValidator();
296                         if(v.ns(ns).err() ||
297                            v.key(key).err() ||
298                            v.value(value).err()) {
299                                 return Result.err(Status.ERR_BadData,v.errs());
300                         }
301
302                         // Check if exists already
303                         Result<List<Data>> rlnsd = ques.nsDAO.read(trans, ns);
304                         if(rlnsd.notOKorIsEmpty()) {
305                                 return Result.err(rlnsd);
306                         }
307                         NsDAO.Data nsd = rlnsd.value.get(0);
308
309                         // Check for Existence
310                         if(nsd.attrib.get(key)!=null) {
311                                 return Result.err(Status.ERR_ConflictAlreadyExists, "NS Property %s:%s exists", ns, key);
312                         }
313                         
314                         // Check if User may put
315                         if(!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
316                                         ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
317                                 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
318                         }
319
320                         // Add Attrib
321                         nsd.attrib.put(key, value);
322                         ques.nsDAO.dao().attribAdd(trans,ns,key,value);
323                         return Result.ok();
324                 } finally {
325                         tt.done();
326                 }
327         }
328         
329         @ApiDoc(
330                         method = GET,  
331                         path = "/authz/ns/attrib/:key",
332                         params = {      "key|string|true" },
333                         expectedCode = 200,
334                         errorCodes = { 403,404 },  
335                         text = {        
336                                 "Read Attributes for Namespace"
337                                 }
338                         )
339         @Override
340         public Result<KEYS> readNsByAttrib(AuthzTrans trans, String key) {
341                 // Check inputs
342                 final Validator v = new ServiceValidator();
343                 if(v.nullOrBlank("Key",key).err()) {
344                           return Result.err(Status.ERR_BadData,v.errs());
345                 }
346
347                 // May Read
348                 if(!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
349                                         ":"+trans.org().getDomain()+".*:"+key, Question.READ)) {
350                         return Result.err(Status.ERR_Denied,"%s may not read NS by Attrib '%s'",trans.user(),key);
351                 }
352
353                 Result<Set<String>> rsd = ques.nsDAO.dao().readNsByAttrib(trans, key);
354                 if(rsd.notOK()) {
355                         return Result.err(rsd);
356                 }
357                 return mapper().keys(rsd.value);
358         }
359
360
361         @ApiDoc(
362                         method = PUT,  
363                         path = "/authz/ns/:ns/attrib/:key/:value",
364                         params = {      "ns|string|true",
365                                                 "key|string|true"},
366                         expectedCode = 200,
367                         errorCodes = { 403,404 },  
368                         text = {        
369                                 "Update Value on an existing attribute in the Namespace",
370                                 "You must be given direct permission for key by AAF"
371                                 }
372                         )
373         @Override
374         public Result<?> updateNsAttrib(AuthzTrans trans, String ns, String key, String value) {
375                 TimeTaken tt = trans.start("Update NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
376                 try {
377                         // Check inputs
378                         final Validator v = new ServiceValidator();
379                         if(v.ns(ns).err() ||
380                            v.key(key).err() ||
381                            v.value(value).err()) {
382                                 return Result.err(Status.ERR_BadData,v.errs());
383                         }
384
385                         // Check if exists already (NS must exist)
386                         Result<List<Data>> rlnsd = ques.nsDAO.read(trans, ns);
387                         if(rlnsd.notOKorIsEmpty()) {
388                                 return Result.err(rlnsd);
389                         }
390                         NsDAO.Data nsd = rlnsd.value.get(0);
391
392                         // Check for Existence
393                         if(nsd.attrib.get(key)==null) {
394                                 return Result.err(Status.ERR_NotFound, "NS Property %s:%s exists", ns, key);
395                         }
396                         
397                         // Check if User may put
398                         if(!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB, 
399                                         ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
400                                 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
401                         }
402
403                         // Add Attrib
404                         nsd.attrib.put(key, value);
405
406                         return ques.nsDAO.update(trans,nsd);
407  
408                 } finally {
409                         tt.done();
410                 }
411         }
412
413         @ApiDoc(
414                         method = DELETE,  
415                         path = "/authz/ns/:ns/attrib/:key",
416                         params = {      "ns|string|true",
417                                                 "key|string|true"},
418                         expectedCode = 200,
419                         errorCodes = { 403,404 },  
420                         text = {        
421                                 "Delete an attribute in the Namespace",
422                                 "You must be given direct permission for key by AAF"
423                                 }
424                         )
425         @Override
426         public Result<Void> deleteNsAttrib(AuthzTrans trans, String ns, String key) {
427                 TimeTaken tt = trans.start("Delete NsAttrib " + ns + ':' + key, Env.SUB);
428                 try {
429                         // Check inputs
430                         final Validator v = new ServiceValidator();
431                         if(v.nullOrBlank("NS",ns).err() ||
432                            v.nullOrBlank("Key",key).err()) {
433                                 return Result.err(Status.ERR_BadData,v.errs());
434                         }
435
436                         // Check if exists already
437                         Result<List<Data>> rlnsd = ques.nsDAO.read(trans, ns);
438                         if(rlnsd.notOKorIsEmpty()) {
439                                 return Result.err(rlnsd);
440                         }
441                         NsDAO.Data nsd = rlnsd.value.get(0);
442
443                         // Check for Existence
444                         if(nsd.attrib.get(key)==null) {
445                                 return Result.err(Status.ERR_NotFound, "NS Property [%s:%s] does not exist", ns, key);
446                         }
447                         
448                         // Check if User may del
449                         if(!ques.isGranted(trans, trans.user(), ROOT_NS, "attrib", ":" + ROOT_COMPANY + ".*:"+key, Access.write.name())) {
450                                 return Result.err(Status.ERR_Denied, "%s may not delete NS Attrib [%s:%s]", trans.user(),ns, key);
451                         }
452
453                         // Add Attrib
454                         nsd.attrib.remove(key);
455                         ques.nsDAO.dao().attribRemove(trans,ns,key);
456                         return Result.ok();
457                 } finally {
458                         tt.done();
459                 }
460         }
461
462         @ApiDoc(
463                         method = GET,  
464                         path = "/authz/nss/:id",
465                         params = {      "id|string|true" },
466                         expectedCode = 200,
467                         errorCodes = { 404,406 }, 
468                         text = {        
469                                 "Lists the Admin(s), Responsible Party(s), Role(s), Permission(s)",
470                                 "Credential(s) and Expiration of Credential(s) in Namespace :id",
471                         }
472                         )
473         @Override
474         public Result<NSS> getNSbyName(AuthzTrans trans, String ns) {
475                 final Validator v = new ServiceValidator();
476                 if(v.nullOrBlank("NS", ns).err()) {
477                         return Result.err(Status.ERR_BadData,v.errs());
478                 }
479                 
480                 Result<List<NsDAO.Data>> rlnd = ques.nsDAO.read(trans, ns);
481                 if(rlnd.isOK()) {
482                         if(rlnd.isEmpty()) {
483                                 return Result.err(Status.ERR_NotFound, "No data found for %s",ns);
484                         }
485                         Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.read);
486                         if(rnd.notOK()) {
487                                 return Result.err(rnd); 
488                         }
489                         
490                         
491                         Namespace namespace = new Namespace(rnd.value);
492                         Result<List<String>> rd = func.getOwners(trans, namespace.name, false);
493                         if(rd.isOK()) {
494                                 namespace.owner = rd.value;
495                         }
496                         rd = func.getAdmins(trans, namespace.name, false);
497                         if(rd.isOK()) {
498                                 namespace.admin = rd.value;
499                         }
500                         
501                         NSS nss = mapper.newInstance(API.NSS);
502                         return mapper.nss(trans, namespace, nss);
503                 } else {
504                         return Result.err(rlnd);
505                 }
506         }
507
508         @ApiDoc(
509                         method = GET,  
510                         path = "/authz/nss/admin/:id",
511                         params = {      "id|string|true" },
512                         expectedCode = 200,
513                         errorCodes = { 403,404 }, 
514                         text = {        "Lists all Namespaces where Identity :id is an Admin", 
515                                                 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" 
516                                         }
517                         )
518         @Override
519         public Result<NSS> getNSbyAdmin(AuthzTrans trans, String user, boolean full) {
520                 final Validator v = new ServiceValidator();
521                 if (v.nullOrBlank("User", user).err()) {
522                         return Result.err(Status.ERR_BadData, v.errs());
523                 }
524                 
525                 Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".admin", full);
526                 if(rn.notOK()) {
527                         return Result.err(rn);
528                 }
529                 if (rn.isEmpty()) {
530                         return Result.err(Status.ERR_NotFound, "[%s] is not an admin for any namespaces",user);         
531                 }
532                 NSS nss = mapper.newInstance(API.NSS);
533                 // Note: "loadNamespace" already validates view of Namespace
534                 return mapper.nss(trans, rn.value, nss);
535
536         }
537
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@people.osaaf.org)" 
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@people.osaaf.org)"
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                 int query = role.indexOf('?');
1578                 Result<List<RoleDAO.Data>> rlrd = ques.getRolesByName(trans, query<0?role:role.substring(0, query));
1579                 if(rlrd.isOK()) {
1580                         // Note: Mapper will restrict what can be viewed
1581                         ROLES roles = mapper.newInstance(API.ROLES);
1582                         return mapper.roles(trans, rlrd.value, roles, true);
1583                 } else {
1584                         return Result.err(rlrd);
1585                 }
1586         }
1587
1588         /* (non-Javadoc)
1589          * @see org.onap.aaf.auth.service.AuthzService#getRolesByUser(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1590          */
1591     @ApiDoc(
1592             method = GET,
1593             path = "/authz/roles/user/:name",
1594             params = {"name|string|true"},
1595             expectedCode = 200,
1596             errorCodes = {404,406},
1597             text = { "List all Roles that match user :name",
1598                                          "'user' must be expressed as full identity (ex: id@full.domain.com)",
1599                                 "Note: You must have permission to see any given role"
1600             }
1601            )
1602
1603         @Override
1604         public Result<ROLES> getRolesByUser(AuthzTrans trans, String user) {
1605                 final Validator v = new ServiceValidator();
1606                 if(v.nullOrBlank("User", user).err()) {
1607                         return Result.err(Status.ERR_BadData,v.errs());
1608                 }
1609
1610                 ROLES roles = mapper.newInstance(API.ROLES);
1611                 // Get list of roles per user, then add to Roles as we go
1612                 Result<List<RoleDAO.Data>> rlrd;
1613                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, user);
1614                 if(rlurd.isOKhasData()) {
1615                         for(UserRoleDAO.Data urd : rlurd.value ) {
1616                                 rlrd = ques.roleDAO.read(trans, urd.ns,urd.rname);
1617                                 // Note: Mapper will restrict what can be viewed
1618                                 //   if user is the same as that which is looked up, no filtering is required
1619                                 if(rlrd.isOKhasData()) {
1620                                         mapper.roles(trans, rlrd.value,roles, !user.equals(trans.user()));
1621                                 }
1622                         }
1623                 }
1624                 return Result.ok(roles);
1625         }
1626
1627         /*
1628          * (non-Javadoc)
1629          * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1630          */
1631     @ApiDoc(
1632             method = GET,
1633             path = "/authz/roles/ns/:ns",
1634             params = {"ns|string|true"},
1635             expectedCode = 200,
1636             errorCodes = {404,406},
1637             text = { "List all Roles for the Namespace :ns", 
1638                                  "Note: You must have permission to see any given role"
1639             }
1640            )
1641
1642         @Override
1643         public Result<ROLES> getRolesByNS(AuthzTrans trans, String ns) {
1644                 final Validator v = new ServiceValidator();
1645                 if(v.nullOrBlank("NS", ns).err()) {
1646                         return Result.err(Status.ERR_BadData,v.errs());
1647                 }
1648                 
1649                 // check if user is allowed to view NS
1650                 Result<NsDAO.Data> rnsd = ques.deriveNs(trans, ns); 
1651                 if(rnsd.notOK()) {
1652                         return Result.err(rnsd);        
1653                 }
1654                 rnsd = ques.mayUser(trans, trans.user(), rnsd.value, Access.read);
1655                 if(rnsd.notOK()) {
1656                         return Result.err(rnsd);        
1657                 }
1658
1659                 TimeTaken tt = trans.start("MAP Roles by NS to Roles", Env.SUB);
1660                 try {
1661                         ROLES roles = mapper.newInstance(API.ROLES);
1662                         // Get list of roles per user, then add to Roles as we go
1663                         Result<List<RoleDAO.Data>> rlrd = ques.roleDAO.readNS(trans, ns);
1664                         if(rlrd.isOK()) {
1665                                 if(!rlrd.isEmpty()) {
1666                                         // Note: Mapper doesn't need to restrict what can be viewed, because we did it already.
1667                                         mapper.roles(trans,rlrd.value,roles,false);
1668                                 }
1669                                 return Result.ok(roles);
1670                         } else {
1671                                 return Result.err(rlrd);
1672                         }
1673                 } finally {
1674                         tt.done();
1675                 }
1676         }
1677
1678         /*
1679          * (non-Javadoc)
1680          * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1681          */
1682     @ApiDoc(
1683             method = GET,
1684             path = "/authz/roles/name/:name",
1685             params = {"name|string|true"},
1686             expectedCode = 200,
1687             errorCodes = {404,406},
1688             text = { "List all Roles for only the Name of Role (without Namespace)", 
1689                                  "Note: You must have permission to see any given role"
1690             }
1691            )
1692         @Override
1693         public Result<ROLES> getRolesByNameOnly(AuthzTrans trans, String name) {
1694                 final Validator v = new ServiceValidator();
1695                 if(v.nullOrBlank("Name", name).err()) {
1696                         return Result.err(Status.ERR_BadData,v.errs());
1697                 }
1698                 
1699                 // User Mapper to make sure user is allowed to view NS
1700
1701                 TimeTaken tt = trans.start("MAP Roles by Name to Roles", Env.SUB);
1702                 try {
1703                         ROLES roles = mapper.newInstance(API.ROLES);
1704                         // Get list of roles per user, then add to Roles as we go
1705                         Result<List<RoleDAO.Data>> rlrd = ques.roleDAO.readName(trans, name);
1706                         if(rlrd.isOK()) {
1707                                 if(!rlrd.isEmpty()) {
1708                                         // Note: Mapper will restrict what can be viewed
1709                                         mapper.roles(trans,rlrd.value,roles,true);
1710                                 }
1711                                 return Result.ok(roles);
1712                         } else {
1713                                 return Result.err(rlrd);
1714                         }
1715                 } finally {
1716                         tt.done();
1717                 }
1718         }
1719
1720     @ApiDoc(
1721             method = GET,
1722             path = "/authz/roles/perm/:type/:instance/:action",
1723             params = {"type|string|true",
1724                       "instance|string|true",
1725                       "action|string|true"},
1726             expectedCode = 200,
1727             errorCodes = {404,406},
1728             text = { "Find all Roles containing the given Permission." +
1729                      "Permission consists of:",
1730                      "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1731                      + "is being protected</li>",
1732                      "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1733                      + " instance of the type</li>",
1734                      "<li>action - what kind of action is allowed</li></ul>",
1735                      "Notes: instance and action can be an *",
1736                          "       You must have permission to see any given role"
1737                      }
1738            )
1739
1740         @Override
1741         public Result<ROLES> getRolesByPerm(AuthzTrans trans, String type, String instance, String action) {
1742                 final Validator v = new ServiceValidator();
1743                 if(v.permType(type)
1744                         .permInstance(instance)
1745                         .permAction(action)
1746                         .err()) {
1747                         return Result.err(Status.ERR_BadData,v.errs());
1748                 }
1749                 
1750                 TimeTaken tt = trans.start("Map Perm Roles Roles", Env.SUB);
1751                 try {
1752                         ROLES roles = mapper.newInstance(API.ROLES);
1753                         // Get list of roles per user, then add to Roles as we go
1754                         Result<NsSplit> nsSplit = ques.deriveNsSplit(trans, type);
1755                         if(nsSplit.isOK()) {
1756                                 PermDAO.Data pdd = new PermDAO.Data(nsSplit.value, instance, action);
1757                                 Result<?> res;
1758                                 if((res=ques.mayUser(trans, trans.user(), pdd, Question.Access.read)).notOK()) {
1759                                         return Result.err(res);
1760                                 }
1761                                 
1762                                 Result<List<PermDAO.Data>> pdlr = ques.permDAO.read(trans, pdd);
1763                                 if(pdlr.isOK())for(PermDAO.Data pd : pdlr.value) {
1764                                         Result<List<RoleDAO.Data>> rlrd;
1765                                         for(String r : pd.roles) {
1766                                                 Result<String[]> rs = RoleDAO.Data.decodeToArray(trans, ques, r);
1767                                                 if(rs.isOK()) {
1768                                                         rlrd = ques.roleDAO.read(trans, rs.value[0],rs.value[1]);
1769                                                         // Note: Mapper will restrict what can be viewed
1770                                                         if(rlrd.isOKhasData()) {
1771                                                                 mapper.roles(trans,rlrd.value,roles,true);
1772                                                         }
1773                                                 }
1774                                         }
1775                                 }
1776                         }
1777                         return Result.ok(roles);
1778                 } finally {
1779                         tt.done();
1780                 }
1781         }
1782
1783     @ApiDoc(
1784             method = PUT,
1785             path = "/authz/role",
1786             params = {},
1787             expectedCode = 200,
1788             errorCodes = {404,406},
1789             text = { "Add Description Data to a Role" }
1790            )
1791
1792         @Override
1793         public Result<Void> updateRoleDescription(AuthzTrans trans, REQUEST from) {
1794                 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1795                 final ServiceValidator v = new ServiceValidator();
1796                 if(v.role(rd).err()) {
1797                         return Result.err(Status.ERR_BadData,v.errs());
1798                 } {
1799                 if(v.nullOrBlank("description", rd.value.description).err()) {
1800                     return Result.err(Status.ERR_BadData,v.errs());
1801                 }
1802                 }
1803                 final RoleDAO.Data role = rd.value;
1804                 if(ques.roleDAO.read(trans, role.ns, role.name).notOKorIsEmpty()) {
1805                         return Result.err(Status.ERR_NotFound, "Role [" + role.fullName() + "] does not exist");
1806                 }
1807
1808                 if (ques.mayUser(trans, trans.user(), role, Access.write).notOK()) {
1809                         return Result.err(Status.ERR_Denied, "You do not have approval to change " + role.fullName());
1810                 }
1811
1812                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rd.value.ns);
1813                 if(nsr.notOKorIsEmpty()) {
1814                         return Result.err(nsr);
1815                 }
1816
1817                 Result<Void> rdr = ques.roleDAO.addDescription(trans, role.ns, role.name, role.description);
1818                 if(rdr.isOK()) {
1819                         return Result.ok();
1820                 } else {
1821                         return Result.err(rdr);
1822                 }
1823
1824         }
1825         
1826     @ApiDoc(
1827             method = POST,
1828             path = "/authz/role/perm",
1829             params = {},
1830             expectedCode = 201,
1831             errorCodes = {403,404,406,409},
1832             text = { "Grant a Permission to a Role",
1833                      "Permission consists of:", 
1834                      "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1835                      + "is being protected</li>",
1836                      "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1837                      + " instance of the type</li>",
1838                      "<li>action - what kind of action is allowed</li></ul>",
1839                      "Note: instance and action can be an *",
1840                      "Note: Using the \"force\" property will create the Permission, if it doesn't exist AND the requesting " +
1841                      " ID is allowed to create.  It will then grant",
1842                      "  the permission to the role in one step. To do this: add 'force=true' as a query parameter."
1843                                         }
1844            )
1845
1846         @Override
1847         public Result<Void> addPermToRole(final AuthzTrans trans, REQUEST rreq) {
1848                 // Translate Request into Perm and Role Objects
1849                 final Result<PermDAO.Data> rpd = mapper.permFromRPRequest(trans, rreq);
1850                 if(rpd.notOKorIsEmpty()) {
1851                         return Result.err(rpd);
1852                 }
1853                 final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
1854                 if(rrd.notOKorIsEmpty()) {
1855                         return Result.err(rrd);
1856                 }
1857                 
1858                 // Validate Role and Perm values
1859                 final ServiceValidator v = new ServiceValidator();
1860                 if(v.perm(rpd.value)
1861                         .role(rrd.value)
1862                         .err()) {
1863                         return Result.err(Status.ERR_BadData,v.errs());
1864                 }
1865
1866                 Result<List<RoleDAO.Data>> rlrd = ques.roleDAO.read(trans, rrd.value.ns, rrd.value.name);
1867                 if(rlrd.notOKorIsEmpty()) {
1868                         return Result.err(Status.ERR_RoleNotFound, "Role [%s] does not exist", rrd.value.fullName());
1869                 }
1870                 
1871                 // Check Status of Data in DB (does it exist)
1872                 Result<List<PermDAO.Data>> rlpd = ques.permDAO.read(trans, rpd.value.ns, 
1873                                 rpd.value.type, rpd.value.instance, rpd.value.action);
1874                 PermDAO.Data createPerm = null; // if not null, create first
1875                 if(rlpd.notOKorIsEmpty()) { // Permission doesn't exist
1876                         if(trans.requested(force)) {
1877                                 // Remove roles from perm data object so we just create the perm here
1878                                 createPerm = rpd.value;
1879                                 createPerm.roles.clear();
1880                         } else {
1881                                 return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist", 
1882                                                 rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action);
1883                         }
1884                 } else {
1885                         if (rlpd.value.get(0).roles(false).contains(rrd.value.encode())) {
1886                                 return Result.err(Status.ERR_ConflictAlreadyExists,
1887                                                 "Permission [%s.%s|%s|%s] already granted to Role [%s.%s]",
1888                                                 rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action,
1889                                                 rrd.value.ns,rrd.value.name
1890                                         );
1891                         }
1892                 }
1893
1894                 
1895                 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, rpd.value,true, // Allow grants to create Approvals
1896                                 new Mapper.Memo() {
1897                                         @Override
1898                                         public String get() {
1899                                                 return "Grant Permission [" + rpd.value.fullPerm() + ']' +
1900                                                         " to Role [" + rrd.value.fullName() + "]";
1901                                         }
1902                                 },
1903                                 new MayChange() {
1904                                         private Result<NsDAO.Data> nsd;
1905                                         @Override
1906                                         public Result<?> mayChange() {
1907                                                 if(nsd==null) {
1908                                                         nsd = ques.mayUser(trans, trans.user(), rpd.value, Access.write);
1909                                                 }
1910                                                 return nsd;
1911                                         }
1912                                 });
1913                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rpd.value.ns);
1914                 if(nsr.notOKorIsEmpty()) {
1915                         return Result.err(nsr);
1916                 }
1917                 switch(fd.status) {
1918                 case OK:
1919                         Result<String> rfc = func.createFuture(trans,fd.value, 
1920                                         rpd.value.fullPerm(),
1921                                         trans.user(),
1922                                         nsr.value.get(0),
1923                                         FUTURE_OP.G);
1924                         if(rfc.isOK()) {
1925                                 return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
1926                                                 rpd.value.ns,
1927                                                 rpd.value.type,
1928                                                 rpd.value.instance,
1929                                                 rpd.value.action);
1930                         } else { 
1931                                 return Result.err(rfc);
1932                         }
1933                 case Status.ACC_Now:
1934                         Result<Void> rv = null;
1935                         if(createPerm!=null) {// has been validated for creating
1936                                 rv = func.createPerm(trans, createPerm, false);
1937                         }
1938                         if(rv==null || rv.isOK()) {
1939                                 rv = func.addPermToRole(trans, rrd.value, rpd.value, false);
1940                         }
1941                         return rv;
1942                 default:
1943                         return Result.err(fd);
1944                 }
1945                 
1946         }
1947
1948         /**
1949          * Delete Perms from Roles (UnGrant)
1950          * @param trans
1951          * @param roleFullName
1952          * @return
1953          */
1954     @ApiDoc(
1955             method = DELETE,
1956             path = "/authz/role/:role/perm",
1957             params = {"role|string|true"},
1958             expectedCode = 200,
1959             errorCodes = {404,406},
1960             text = { "Ungrant a permission from Role :role" }
1961            )
1962
1963         @Override
1964         public Result<Void> delPermFromRole(final AuthzTrans trans, REQUEST rreq) {
1965                 final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
1966                 if(updt.notOKorIsEmpty()) {
1967                         return Result.err(updt);
1968                 }
1969                 final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
1970                 if(rrd.notOKorIsEmpty()) {
1971                         return Result.err(rrd);
1972                 }
1973
1974                 final ServiceValidator v = new ServiceValidator();
1975                 if(v.nullOrBlank(updt.value)
1976                         .nullOrBlank(rrd.value)
1977                         .err()) {
1978                         return Result.err(Status.ERR_BadData,v.errs());
1979                 }
1980
1981                 return delPermFromRole(trans, updt.value,rrd.value, rreq);
1982     }
1983                 
1984         private Result<Void> delPermFromRole(final AuthzTrans trans, PermDAO.Data pdd, RoleDAO.Data rdd, REQUEST rreq) {                
1985                 Result<List<PermDAO.Data>> rlpd = ques.permDAO.read(trans, pdd.ns, pdd.type, 
1986                                 pdd.instance, pdd.action);
1987                 
1988                 if(rlpd.notOKorIsEmpty()) {
1989                         return Result.err(Status.ERR_PermissionNotFound, 
1990                                 "Permission [%s.%s|%s|%s] does not exist",
1991                                         pdd.ns,pdd.type,pdd.instance,pdd.action);
1992                 }
1993                 
1994                 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, pdd,true, // allow ungrants requests
1995                                 new Mapper.Memo() {
1996                                         @Override
1997                                         public String get() {
1998                                                 return "Ungrant Permission [" + pdd.fullPerm() + ']' +
1999                                                         " from Role [" + rdd.fullName() + "]";
2000                                         }
2001                                 },
2002                                 new MayChange() {
2003                                         private Result<NsDAO.Data> nsd;
2004                                         @Override
2005                                         public Result<?> mayChange() {
2006                                                 if(nsd==null) {
2007                                                         nsd = ques.mayUser(trans, trans.user(), pdd, Access.write);
2008                                                 }
2009                                                 return nsd;
2010                                         }
2011                                 });
2012                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, pdd.ns);
2013                 if(nsr.notOKorIsEmpty()) {
2014                         return Result.err(nsr);
2015                 }
2016                 switch(fd.status) {
2017                         case OK:
2018                                 Result<String> rfc = func.createFuture(trans,fd.value, 
2019                                                 pdd.fullPerm(),
2020                                                 trans.user(),
2021                                                 nsr.value.get(0),
2022                                                 FUTURE_OP.UG
2023                                                 );
2024                                 if(rfc.isOK()) {
2025                                         return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
2026                                                         pdd.ns,
2027                                                         pdd.type,
2028                                                         pdd.instance,
2029                                                         pdd.action);
2030                                 } else {
2031                                     return Result.err(rfc);
2032                                 }
2033                         case Status.ACC_Now:
2034                                 return func.delPermFromRole(trans, rdd, pdd, false);
2035                         default:
2036                                 return Result.err(fd);
2037                 }
2038         }
2039     
2040 /*
2041     @ApiDoc(
2042             method = DELETE,
2043             path = "/authz/role/:role/perm/:type/:instance/:action",
2044             params = {"role|string|true",
2045                                  "perm type|string|true",
2046                                  "perm instance|string|true",
2047                                  "perm action|string|true"
2048                 },
2049             expectedCode = 200,
2050             errorCodes = {404,406},
2051             text = { "Ungrant a single permission from Role :role with direct key" }
2052            )
2053 */
2054         @Override
2055     public Result<Void> delPermFromRole(AuthzTrans trans, String role, String type, String instance, String action) {
2056                 Result<Data> rpns = ques.deriveNs(trans, type);
2057                 if(rpns.notOKorIsEmpty()) {
2058                         return Result.err(rpns);
2059                 }
2060                 
2061                 final Validator v = new ServiceValidator();
2062                 if(v.role(role)
2063                         .permType(rpns.value.name,rpns.value.parent)
2064                         .permInstance(instance)
2065                         .permAction(action)
2066                         .err()) {
2067                         return Result.err(Status.ERR_BadData,v.errs());
2068                 }
2069                 
2070                 Result<Data> rrns = ques.deriveNs(trans, role);
2071                 if(rrns.notOKorIsEmpty()) {
2072                         return Result.err(rrns);
2073                 }
2074                 
2075                 final Result<List<RoleDAO.Data>> rrd = ques.roleDAO.read(trans, rrns.value.parent, rrns.value.name);
2076                 if(rrd.notOKorIsEmpty()) {
2077                         return Result.err(rrd);
2078                 }
2079                 
2080                 final Result<List<PermDAO.Data>> rpd = ques.permDAO.read(trans, rpns.value.parent, rpns.value.name, instance, action);
2081                 if(rpd.notOKorIsEmpty()) {
2082                         return Result.err(rpd);
2083                 }
2084
2085                 
2086                 return delPermFromRole(trans,rpd.value.get(0), rrd.value.get(0), mapper.ungrantRequest(trans, role, type, instance, action));
2087         }
2088         
2089     @ApiDoc(
2090             method = DELETE,
2091             path = "/authz/role/:role",
2092             params = {"role|string|true"},
2093             expectedCode = 200,
2094             errorCodes = {404,406},
2095             text = { "Delete the Role named :role"}
2096            )
2097
2098         @Override
2099         public Result<Void> deleteRole(AuthzTrans trans, String role)  {
2100                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
2101                 if(rrdd.isOKhasData()) {
2102                         final ServiceValidator v = new ServiceValidator();
2103                         if(v.nullOrBlank(rrdd.value).err()) { 
2104                                 return Result.err(Status.ERR_BadData,v.errs());
2105                         }
2106                         return func.deleteRole(trans, rrdd.value, false, false);
2107                 } else {
2108                         return Result.err(rrdd);
2109                 }
2110         }
2111
2112     @ApiDoc(
2113             method = DELETE,
2114             path = "/authz/role",
2115             params = {},
2116             expectedCode = 200,
2117             errorCodes = { 404,406 },
2118             text = { "Delete the Role referenced by RoleKey",
2119                                         "You cannot normally delete a role which still has permissions granted or users assigned to it,",
2120                                         "however the \"force\" property allows you to do just that. To do this: Add 'force=true'",
2121                                         "as a query parameter.",
2122                                         "<p>WARNING: Using force will remove all users and permission from this role. Use with care.</p>"}
2123            )
2124
2125         @Override
2126         public Result<Void> deleteRole(final AuthzTrans trans, REQUEST from) {
2127                 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
2128                 final ServiceValidator v = new ServiceValidator();
2129                 if(rd==null) {
2130                         return Result.err(Status.ERR_BadData,"Request does not contain Role");
2131                 }
2132                 if(v.nullOrBlank(rd.value).err()) {
2133                         return Result.err(Status.ERR_BadData,v.errs());
2134                 }
2135                 final RoleDAO.Data role = rd.value;
2136                 if(ques.roleDAO.read(trans, role).notOKorIsEmpty() && !trans.requested(force)) {
2137                         return Result.err(Status.ERR_RoleNotFound, "Role [" + role.fullName() + "] does not exist");
2138                 }
2139
2140                 Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
2141                                 new Mapper.Memo() {
2142                                         @Override
2143                                         public String get() {
2144                                                 return "Delete Role [" + role.fullName() + ']' 
2145                                                                 + " and all attached user roles";
2146                                         }
2147                                 },
2148                         new MayChange() {
2149                                 private Result<NsDAO.Data> nsd;
2150                                 @Override
2151                                 public Result<?> mayChange() {
2152                                         if(nsd==null) {
2153                                                 nsd = ques.mayUser(trans, trans.user(), role, Access.write);
2154                                         }
2155                                         return nsd;
2156                                 }
2157                         });
2158                 
2159                 switch(fd.status) {
2160                 case OK:
2161                         Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rd.value.ns);
2162                         if(nsr.notOKorIsEmpty()) {
2163                                 return Result.err(nsr);
2164                         }
2165                         
2166                         Result<String> rfc = func.createFuture(trans, fd.value, 
2167                                         role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
2168                         if(rfc.isOK()) {
2169                                 return Result.err(Status.ACC_Future, "Role Deletion [%s.%s] is saved for future processing",
2170                                                 rd.value.ns,
2171                                                 rd.value.name);
2172                         } else { 
2173                                 return Result.err(rfc);
2174                         }
2175                 case Status.ACC_Now:
2176                         return func.deleteRole(trans,role,trans.requested(force), true /*preapproved*/);
2177                 default:
2178                         return Result.err(fd);
2179         }
2180
2181         }
2182
2183 /***********************************
2184  * CRED 
2185  ***********************************/
2186         private class MayCreateCred implements MayChange {
2187                 private Result<NsDAO.Data> nsd;
2188                 private AuthzTrans trans;
2189                 private CredDAO.Data cred;
2190                 private Executor exec;
2191                 
2192                 public MayCreateCred(AuthzTrans trans, CredDAO.Data cred, Executor exec) {
2193                         this.trans = trans;
2194                         this.cred = cred;
2195                         this.exec = exec;
2196                 }
2197
2198                 @Override
2199                 public Result<?> mayChange() {
2200                         if(nsd==null) {
2201                                 nsd = ques.validNSOfDomain(trans, cred.id);
2202                         }
2203                         // is Ns of CredID valid?
2204                         if(nsd.isOK()) {
2205                                 try {
2206                                         // Check Org Policy
2207                                         if(trans.org().validate(trans,Policy.CREATE_MECHID, exec, cred.id)==null) {
2208                                                 return Result.ok(); 
2209                                         } else {
2210                                            Result<?> rmc = ques.mayUser(trans, trans.user(), nsd.value, Access.write);
2211                                            if(rmc.isOKhasData()) {
2212                                                    return rmc;
2213                                            }
2214                                         }
2215                                 } catch (Exception e) {
2216                                         trans.warn().log(e);
2217                                 }
2218                         } else {
2219                                 trans.warn().log(nsd.errorString());
2220                         }
2221                         return Result.err(Status.ERR_Denied,"%s is not allowed to create %s in %s",trans.user(),cred.id,cred.ns);
2222                 }
2223         }
2224
2225         private class MayChangeCred implements MayChange {
2226                 
2227                 private Result<NsDAO.Data> nsd;
2228                 private AuthzTrans trans;
2229                 private CredDAO.Data cred;
2230                 public MayChangeCred(AuthzTrans trans, CredDAO.Data cred) {
2231                         this.trans = trans;
2232                         this.cred = cred;
2233                 }
2234
2235                 @Override
2236                 public Result<?> mayChange() {
2237                         // User can change himself (but not create)
2238                         if(trans.user().equals(cred.id)) {
2239                                 return Result.ok();
2240                         }
2241                         if(nsd==null) {
2242                                 nsd = ques.validNSOfDomain(trans, cred.id);
2243                         }
2244                         // Get the Namespace
2245                         if(nsd.isOK()) {
2246                                 if(ques.mayUser(trans, trans.user(), nsd.value,Access.write).isOK()) {
2247                                         return Result.ok();
2248                                 }
2249                                 String user[] = Split.split('.',trans.user());
2250                                 if(user.length>2) {
2251                                         String company = user[user.length-1] + '.' + user[user.length-2];
2252                                         if(ques.isGranted(trans, trans.user(), ROOT_NS,"password",company,"reset")) {
2253                                                 return Result.ok();
2254                                         }
2255                                 }
2256                         }
2257                         return Result.err(Status.ERR_Denied,"%s is not allowed to change %s in %s",trans.user(),cred.id,cred.ns);
2258                 }
2259
2260         }
2261
2262         private final long DAY_IN_MILLIS = 24*3600*1000L;
2263         
2264         @ApiDoc( 
2265                         method = POST,  
2266                         path = "/authn/cred",
2267                         params = {},
2268                         expectedCode = 201,
2269                         errorCodes = {403,404,406,409}, 
2270                         text = { "A credential consists of:",
2271                                          "<ul><li>id - the ID to create within AAF. The domain is in reverse",
2272                                          "order of Namespace (i.e. Users of Namespace com.att.myapp would be",
2273                                          "AB1234@myapp.att.com</li>",
2274                                          "<li>password - Company Policy Compliant Password</li></ul>",
2275                                          "Note: AAF does support multiple credentials with the same ID.",
2276                                          "Check with your organization if you have this implemented."
2277                                          }
2278                         )
2279         @Override
2280         public Result<Void> createUserCred(final AuthzTrans trans, REQUEST from) {
2281                 final String cmdDescription = ("Create User Credential");
2282                 TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2283                 
2284                 try {
2285                         Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2286                         if(rcred.isOKhasData()) {
2287                                 byte[] rawCred = rcred.value.cred.array();
2288                                 rcred = ques.userCredSetup(trans, rcred.value);
2289                                 
2290                                 final ServiceValidator v = new ServiceValidator();
2291                                 
2292                                 if(v.cred(trans, trans.org(),rcred,true).err()) { // Note: Creates have stricter Validations 
2293                                         return Result.err(Status.ERR_BadData,v.errs());
2294                                 }
2295                                 
2296
2297                                 // 2016-4 Jonathan, New Behavior - If MechID is not registered with Org, deny creation
2298                                 Identity mechID =  null;
2299                                 Organization org = trans.org();
2300                                 try {
2301                                         mechID = org.getIdentity(trans, rcred.value.id);
2302                                 } catch (Exception e1) {
2303                                         trans.error().log(e1,rcred.value.id,"cannot be validated at this time");
2304                                 }
2305                                 if(mechID==null || !mechID.isFound()) { 
2306                                         return Result.err(Status.ERR_Policy,"MechIDs must be registered with %s before provisioning in AAF",org.getName());
2307                                 }
2308
2309                                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rcred.value.ns);
2310                                 if(nsr.notOKorIsEmpty()) {
2311                                         return Result.err(Status.ERR_NsNotFound,"Cannot provision %s on non-existent Namespace %s",mechID.id(),rcred.value.ns);
2312                                 }
2313                                 
2314
2315                                 boolean firstID = false;
2316                                 MayChange mc;
2317                                 
2318                                 CassExecutor exec = new CassExecutor(trans, func);
2319                                 Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, rcred.value.id);
2320                                 if (rlcd.isOKhasData()) {
2321                                         if (!org.canHaveMultipleCreds(rcred.value.id)) {
2322                                                 return Result.err(Status.ERR_ConflictAlreadyExists, "Credential exists");
2323                                         }
2324                                         Result<Boolean> rb;
2325                                         for (CredDAO.Data curr : rlcd.value) {
2326                                                 // May not use the same password in the list
2327                                                 // Note: ASPR specifies character differences, but we don't actually store the
2328                                                 // password to validate char differences.
2329                                                 
2330                                                 rb = ques.userCredCheck(trans, curr, rawCred);
2331                                                 if(rb.notOK()) {
2332                                                         return Result.err(rb);
2333                                                 } else if(rb.value){
2334                                                         return Result.err(Status.ERR_Policy, "Credential content cannot be reused.");
2335                                                 } else if (Chrono.dateOnlyStamp(curr.expires).equals(Chrono.dateOnlyStamp(rcred.value.expires)) && curr.type==rcred.value.type) {
2336                                                         return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists, use 'reset'");
2337                                                 }
2338                                         }       
2339                                 } else {
2340                                         try {
2341                                         // 2016-04-12 Jonathan If Caller is the Sponsor and is also an Owner of NS, allow without special Perm
2342                                                 String theMechID = rcred.value.id;
2343                                                 Boolean otherMechIDs = false;
2344                                                 // find out if this is the only mechID.  other MechIDs mean special handling (not automated)
2345                                                 for(CredDAO.Data cd : ques.credDAO.readNS(trans,nsr.value.get(0).name).value) {
2346                                                         if(!cd.id.equals(theMechID)) {
2347                                                                 otherMechIDs = true;
2348                                                                 break;
2349                                                         }
2350                                                 }
2351                                                 String reason;
2352                                                 // We can say "ID does not exist" here
2353                                                 if((reason=org.validate(trans, Policy.CREATE_MECHID, exec, theMechID,trans.user(),otherMechIDs.toString()))!=null) {
2354                                                         return Result.err(Status.ERR_Denied, reason); 
2355                                                 }
2356                                                 firstID=true;
2357                                         } catch (Exception e) {
2358                                                 return Result.err(e);
2359                                         }
2360                                 }
2361         
2362                                 mc = new MayCreateCred(trans, rcred.value, exec);
2363                                 
2364                                 final CredDAO.Data cdd = rcred.value;
2365                                 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false, // may want to enable in future.
2366                                         new Mapper.Memo() {
2367                                                 @Override
2368                                                 public String get() {
2369                                                         return cmdDescription + " [" + 
2370                                                                 cdd.id + '|' 
2371                                                                 + cdd.type + '|' 
2372                                                                 + cdd.expires + ']';
2373                                                 }
2374                                         },
2375                                         mc);
2376                                 
2377                                 switch(fd.status) {
2378                                         case OK:
2379                                                 Result<String> rfc = func.createFuture(trans, fd.value, 
2380                                                                 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2381                                                                 trans.user(), nsr.value.get(0), FUTURE_OP.C);
2382                                                 if(rfc.isOK()) {
2383                                                         return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s] is saved for future processing",
2384                                                                         rcred.value.id,
2385                                                                         Integer.toString(rcred.value.type),
2386                                                                         rcred.value.expires.toString());
2387                                                 } else { 
2388                                                         return Result.err(rfc);
2389                                                 }
2390                                         case Status.ACC_Now:
2391                                                 try {
2392                                                         if(firstID) {
2393         //                                                      && !nsr.value.get(0).isAdmin(trans.getUserPrincipal().getName())) {
2394                                                                 Result<List<String>> admins = func.getAdmins(trans, nsr.value.get(0).name, false);
2395                                                                 // OK, it's a first ID, and not by NS Admin, so let's set TempPassword length
2396                                                                 // Note, we only do this on First time, because of possibility of 
2397                                                                 // prematurely expiring a production id
2398                                                                 if(admins.isOKhasData() && !admins.value.contains(trans.user())) {
2399                                                                         rcred.value.expires = org.expiration(null, Expiration.TempPassword).getTime();
2400                                                                 }
2401                                                         }
2402                                                 } catch (Exception e) {
2403                                                         trans.error().log(e, "While setting expiration to TempPassword");
2404                                                 }
2405                                                 Result<?>udr = ques.credDAO.create(trans, rcred.value);
2406                                                 if(udr.isOK()) {
2407                                                         return Result.ok();
2408                                                 }
2409                                                 return Result.err(udr);
2410                                         default:
2411                                                 return Result.err(fd);
2412                                 }
2413
2414                         } else {
2415                                 return Result.err(rcred);
2416                         }
2417                 } finally {
2418                         tt.done();
2419                 }
2420         }
2421
2422         @ApiDoc(   
2423                         method = GET,  
2424                         path = "/authn/creds/ns/:ns",
2425                         params = {"ns|string|true"},
2426                         expectedCode = 200,
2427                         errorCodes = {403,404,406}, 
2428                         text = { "Return all IDs in Namespace :ns"
2429                                          }
2430                         )
2431         @Override
2432         public Result<USERS> getCredsByNS(AuthzTrans trans, String ns) {
2433                 final Validator v = new ServiceValidator();
2434                 if(v.ns(ns).err()) {
2435                         return Result.err(Status.ERR_BadData,v.errs());
2436                 }
2437                 
2438                 // check if user is allowed to view NS
2439                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2440                 if(rnd.notOK()) {
2441                         return Result.err(rnd); 
2442                 }
2443                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2444                 if(rnd.notOK()) {
2445                         return Result.err(rnd); 
2446                 }
2447         
2448                 TimeTaken tt = trans.start("MAP Creds by NS to Creds", Env.SUB);
2449                 try {                   
2450                         USERS users = mapper.newInstance(API.USERS);
2451                         Result<List<CredDAO.Data>> rlcd = ques.credDAO.readNS(trans, ns);
2452                                         
2453                         if(rlcd.isOK()) {
2454                                 if(!rlcd.isEmpty()) {
2455                                         return mapper.cred(rlcd.value, users);
2456                                 }
2457                                 return Result.ok(users);                
2458                         } else {
2459                                 return Result.err(rlcd);
2460                         }
2461                 } finally {
2462                         tt.done();
2463                 }
2464                         
2465         }
2466
2467         @ApiDoc(   
2468                         method = GET,  
2469                         path = "/authn/creds/id/:ns",
2470                         params = {"id|string|true"},
2471                         expectedCode = 200,
2472                         errorCodes = {403,404,406}, 
2473                         text = { "Return all IDs in for ID"
2474                                         ,"(because IDs are multiple, due to multiple Expiration Dates)"
2475                                          }
2476                         )
2477         @Override
2478         public Result<USERS> getCredsByID(AuthzTrans trans, String id) {
2479                 final Validator v = new ServiceValidator();
2480                 if(v.nullOrBlank("ID",id).err()) {
2481                         return Result.err(Status.ERR_BadData,v.errs());
2482                 }
2483                 
2484                 String ns = Question.domain2ns(id);
2485                 // check if user is allowed to view NS
2486                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2487                 if(rnd.notOK()) {
2488                         return Result.err(rnd); 
2489                 }
2490                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2491                 if(rnd.notOK()) {
2492                         return Result.err(rnd); 
2493                 }
2494         
2495                 TimeTaken tt = trans.start("MAP Creds by ID to Creds", Env.SUB);
2496                 try {                   
2497                         USERS users = mapper.newInstance(API.USERS);
2498                         Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, id);
2499                                         
2500                         if(rlcd.isOK()) {
2501                                 if(!rlcd.isEmpty()) {
2502                                         return mapper.cred(rlcd.value, users);
2503                                 }
2504                                 return Result.ok(users);                
2505                         } else {
2506                                 return Result.err(rlcd);
2507                         }
2508                 } finally {
2509                         tt.done();
2510                 }
2511                         
2512         }
2513
2514         @ApiDoc(   
2515                         method = GET,  
2516                         path = "/authn/certs/id/:id",
2517                         params = {"id|string|true"},
2518                         expectedCode = 200,
2519                         errorCodes = {403,404,406}, 
2520                         text = { "Return Cert Info for ID"
2521                                    }
2522                         )
2523         @Override
2524         public Result<CERTS> getCertInfoByID(AuthzTrans trans, HttpServletRequest req, String id) {
2525                 TimeTaken tt = trans.start("Get Cert Info by ID", Env.SUB);
2526                 try {                   
2527                         CERTS certs = mapper.newInstance(API.CERTS);
2528                         Result<List<CertDAO.Data>> rlcd = ques.certDAO.readID(trans, id);
2529                                         
2530                         if(rlcd.isOK()) {
2531                                 if(!rlcd.isEmpty()) {
2532                                         return mapper.cert(rlcd.value, certs);
2533                                 }
2534                                 return Result.ok(certs);                
2535                         } else { 
2536                                 return Result.err(rlcd);
2537                         }
2538                 } finally {
2539                         tt.done();
2540                 }
2541
2542         }
2543
2544         @ApiDoc( 
2545                         method = PUT,  
2546                         path = "/authn/cred",
2547                         params = {},
2548                         expectedCode = 200,
2549                         errorCodes = {300,403,404,406}, 
2550                         text = { "Reset a Credential Password. If multiple credentials exist for this",
2551                                                 "ID, you will need to specify which entry you are resetting in the",
2552                                                 "CredRequest object"
2553                                          }
2554                         )
2555         @Override
2556         public Result<Void> changeUserCred(final AuthzTrans trans, REQUEST from) {
2557                 final String cmdDescription = "Update User Credential";
2558                 TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2559                 try {
2560                         Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2561                         if(rcred.isOKhasData()) {
2562                                 rcred = ques.userCredSetup(trans, rcred.value);
2563         
2564                                 final ServiceValidator v = new ServiceValidator();
2565                                 
2566                                 if(v.cred(trans, trans.org(),rcred,false).err()) {// Note: Creates have stricter Validations 
2567                                         return Result.err(Status.ERR_BadData,v.errs());
2568                                 }
2569                                 Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, rcred.value.id);
2570                                 if(rlcd.notOKorIsEmpty()) {
2571                                         return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2572                                 } 
2573                                 
2574                                 MayChange mc = new MayChangeCred(trans, rcred.value);
2575                                 Result<?> rmc = mc.mayChange(); 
2576                                 if (rmc.notOK()) {
2577                                         return Result.err(rmc);
2578                                 }
2579                                 
2580                                 Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, rlcd.value);
2581                                 if(ri.notOK()) {
2582                                         return Result.err(ri);
2583                                 }
2584                                 int entry = ri.value;
2585         
2586                                 
2587                                 final CredDAO.Data cred = rcred.value;
2588                                 
2589                                 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false,
2590                                 new Mapper.Memo() {
2591                                         @Override
2592                                         public String get() {
2593                                                 return cmdDescription + " [" + 
2594                                                         cred.id + '|' 
2595                                                         + cred.type + '|' 
2596                                                         + cred.expires + ']';
2597                                         }
2598                                 },
2599                                 mc);
2600                                 
2601                                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, rcred.value.ns);
2602                                 if(nsr.notOKorIsEmpty()) {
2603                                         return Result.err(nsr);
2604                                 }
2605         
2606                                 switch(fd.status) {
2607                                         case OK:
2608                                                 Result<String> rfc = func.createFuture(trans, fd.value, 
2609                                                                 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2610                                                                 trans.user(), nsr.value.get(0), FUTURE_OP.U);
2611                                                 if(rfc.isOK()) {
2612                                                         return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s]",
2613                                                                         rcred.value.id,
2614                                                                         Integer.toString(rcred.value.type),
2615                                                                         rcred.value.expires.toString());
2616                                                 } else { 
2617                                                         return Result.err(rfc);
2618                                                 }
2619                                         case Status.ACC_Now:
2620                                                 Result<?>udr = null;
2621                                                 // If we are Resetting Password on behalf of someone else (am not the Admin)
2622                                                 //  use TempPassword Expiration time.
2623                                                 Expiration exp;
2624                                                 if(ques.isAdmin(trans, trans.user(), nsr.value.get(0).name)) {
2625                                                         exp = Expiration.Password;
2626                                                 } else {
2627                                                         exp = Expiration.TempPassword;
2628                                                 }
2629                                                 
2630                                                 Organization org = trans.org();
2631                                                 CredDAO.Data current = rlcd.value.get(entry);
2632                                                 // If user resets password in same day, we will have a primary key conflict, so subtract 1 day
2633                                                 if (current.expires.equals(rcred.value.expires) 
2634                                                                         && rlcd.value.get(entry).type==rcred.value.type) {
2635                                                         GregorianCalendar gc = org.expiration(null, exp,rcred.value.id);
2636                                                         gc = Chrono.firstMomentOfDay(gc);
2637                                                         gc.set(GregorianCalendar.HOUR_OF_DAY, org.startOfDay());                                                
2638                                                         rcred.value.expires = new Date(gc.getTimeInMillis() - DAY_IN_MILLIS);
2639                                                 } else {
2640                                                         rcred.value.expires = org.expiration(null,exp).getTime();
2641                                                 }
2642                                                 // Copy in other fields 10/21/2016
2643                                                 rcred.value.notes=current.notes;
2644
2645                                                 udr = ques.credDAO.create(trans, rcred.value);
2646                                                 if(udr.isOK()) {
2647                                                         udr = ques.credDAO.delete(trans, rlcd.value.get(entry),false);
2648                                                 }
2649                                                 if (udr.isOK()) {
2650                                                         return Result.ok();
2651                                                 }
2652         
2653                                                 return Result.err(udr);
2654                                         default:
2655                                                 return Result.err(fd);
2656                                 }
2657                         } else {
2658                                 return Result.err(rcred);
2659                         }
2660                 } finally {
2661                         tt.done();
2662                 }
2663         }
2664
2665         /*
2666          * Codify the way to get Either Choice Needed or actual Integer from Credit Request
2667          */
2668         private Result<Integer> selectEntryIfMultiple(final CredRequest cr, List<CredDAO.Data> lcd) {
2669                 int entry = 0;
2670                 if (lcd.size() > 1) {
2671                         String inputOption = cr.getEntry();
2672                         if (inputOption == null) {
2673                                 String message = selectCredFromList(lcd, false);
2674                                 String[] variables = buildVariables(lcd);
2675                                 return Result.err(Status.ERR_ChoiceNeeded, message, variables);
2676                         } else {
2677                             entry = Integer.parseInt(inputOption) - 1;
2678                         }
2679                         if (entry < 0 || entry >= lcd.size()) {
2680                                 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2681                         }
2682                 }
2683                 return Result.ok(entry);
2684         }
2685         
2686         @ApiDoc( 
2687                         method = PUT,  
2688                         path = "/authn/cred/:days",
2689                         params = {"days|string|true"},
2690                         expectedCode = 200,
2691                         errorCodes = {300,403,404,406}, 
2692                         text = { "Extend a Credential Expiration Date. The intention of this API is",
2693                                                 "to avoid an outage in PROD due to a Credential expiring before it",
2694                                                 "can be configured correctly. Measures are being put in place ",
2695                                                 "so that this is not abused."
2696                                          }
2697                         )
2698         @Override
2699         public Result<Void> extendUserCred(final AuthzTrans trans, REQUEST from, String days) {
2700                 TimeTaken tt = trans.start("Extend User Credential", Env.SUB);
2701                 try {
2702                         Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2703                         Organization org = trans.org();
2704                         final ServiceValidator v = new ServiceValidator();
2705                         if(v.notOK(cred).err() || 
2706                            v.nullOrBlank(cred.value.id, "Invalid ID").err() ||
2707                            v.user(org,cred.value.id).err())  {
2708                                  return Result.err(Status.ERR_BadData,v.errs());
2709                         }
2710                         
2711                         try {
2712                                 String reason;
2713                                 if ((reason=org.validate(trans, Policy.MAY_EXTEND_CRED_EXPIRES, new CassExecutor(trans,func)))!=null) {
2714                                         return Result.err(Status.ERR_Policy,reason);
2715                                 }
2716                         } catch (Exception e) {
2717                                 String msg;
2718                                 trans.error().log(e, msg="Could not contact Organization for User Validation");
2719                                 return Result.err(Status.ERR_Denied, msg);
2720                         }
2721         
2722                         // Get the list of Cred Entries
2723                         Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, cred.value.id);
2724                         if(rlcd.notOKorIsEmpty()) {
2725                                 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2726                         }
2727
2728                         //Need to do the "Pick Entry" mechanism
2729                         Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, rlcd.value);
2730                         if(ri.notOK()) {
2731                                 return Result.err(ri);
2732                         }
2733
2734                         CredDAO.Data found = rlcd.value.get(ri.value);
2735                         CredDAO.Data cd = cred.value;
2736                         // Copy over the cred
2737                         cd.id = found.id;
2738                         cd.cred = found.cred;
2739                         cd.other = found.other;
2740                         cd.type = found.type;
2741                         cd.notes = found.notes;
2742                         cd.ns = found.ns;
2743                         cd.expires = org.expiration(null, Expiration.ExtendPassword,days).getTime();
2744                         
2745                         cred = ques.credDAO.create(trans, cd);
2746                         if(cred.isOK()) {
2747                                 return Result.ok();
2748                         }
2749                         return Result.err(cred);
2750                 } finally {
2751                         tt.done();
2752                 }
2753         }       
2754
2755         private String[] buildVariables(List<CredDAO.Data> value) {
2756                 // ensure credentials are sorted so we can fully automate Cred regression test
2757                 Collections.sort(value, new Comparator<CredDAO.Data>() {
2758                         @Override
2759                         public int compare(CredDAO.Data cred1, CredDAO.Data cred2) {
2760                                 return cred1.expires.compareTo(cred2.expires);
2761                         }                       
2762                 });
2763                 String [] vars = new String[value.size()+1];
2764                 vars[0]="Choice";
2765                 for (int i = 0; i < value.size(); i++) {
2766                 vars[i+1] = value.get(i).id + "    " + value.get(i).type 
2767                                 + "    |" + value.get(i).expires;
2768                 }
2769                 return vars;
2770         }
2771         
2772         private String selectCredFromList(List<CredDAO.Data> value, boolean isDelete) {
2773                 StringBuilder errMessage = new StringBuilder();
2774                 String userPrompt = isDelete?"Select which cred to delete (set force=true to delete all):":"Select which cred to update:";
2775                 int numSpaces = value.get(0).id.length() - "Id".length();
2776                 
2777                 errMessage.append(userPrompt + '\n');
2778                 errMessage.append("       Id");
2779                 for (int i = 0; i < numSpaces; i++) {
2780                     errMessage.append(' ');
2781                 }
2782                 errMessage.append("   Type  Expires" + '\n');
2783                 for(int i=0;i<value.size();++i) {
2784                         errMessage.append("    %s\n");
2785                 }
2786                 errMessage.append("Run same command again with chosen entry as last parameter");
2787                 
2788                 return errMessage.toString();
2789                 
2790         }
2791
2792         @ApiDoc( 
2793                         method = DELETE,  
2794                         path = "/authn/cred",
2795                         params = {},
2796                         expectedCode = 200,
2797                         errorCodes = {300,403,404,406}, 
2798                         text = { "Delete a Credential. If multiple credentials exist for this",
2799                                         "ID, you will need to specify which entry you are deleting in the",
2800                                         "CredRequest object."
2801                                          }
2802                         )
2803         @Override
2804         public Result<Void> deleteUserCred(AuthzTrans trans, REQUEST from)  {
2805                 final Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2806                 final Validator v = new ServiceValidator();
2807                 if(v.nullOrBlank("cred", cred.value.id).err()) {
2808                         return Result.err(Status.ERR_BadData,v.errs());
2809                 }
2810         
2811                 Result<List<CredDAO.Data>> rlcd = ques.credDAO.readID(trans, cred.value.id);
2812                 if(rlcd.notOKorIsEmpty()) {
2813                         // Empty Creds should have no user_roles.
2814                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, cred.value.id);
2815                         if(rlurd.isOK()) {
2816                                 for(UserRoleDAO.Data data : rlurd.value) {
2817                                         ques.userRoleDAO.delete(trans, data, false);
2818                                 }
2819                         }
2820                         return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2821                 }
2822                 boolean isLastCred = rlcd.value.size()==1;
2823                 
2824                 MayChange mc = new MayChangeCred(trans,cred.value);
2825                 Result<?> rmc = mc.mayChange(); 
2826                 if (rmc.notOK()) {
2827                         return Result.err(rmc);
2828                 }
2829                 
2830                 int entry = 0;
2831                 if(!trans.requested(force)) {
2832                         if (rlcd.value.size() > 1) {
2833                                 CredRequest cr = (CredRequest)from;
2834                                 String inputOption = cr.getEntry();
2835                                 if (inputOption == null) {
2836                                         String message = selectCredFromList(rlcd.value, true);
2837                                         String[] variables = buildVariables(rlcd.value);
2838                                         return Result.err(Status.ERR_ChoiceNeeded, message, variables);
2839                                 } else {
2840                                         try {
2841                                                 if(inputOption.length()>5) { // should be a date
2842                                                         Date d = Chrono.xmlDatatypeFactory.newXMLGregorianCalendar(inputOption).toGregorianCalendar().getTime();
2843                                                         entry = 0;
2844                                                         for(CredDAO.Data cd : rlcd.value) {
2845                                                                 if(cd.type.equals(cr.getType()) && cd.expires.equals(d)) {
2846                                                                         break;
2847                                                                 }
2848                                                                 ++entry;
2849                                                         }
2850                                                 } else {
2851                                                         entry = Integer.parseInt(inputOption) - 1;
2852                                                 }
2853                                         } catch(NullPointerException e) {
2854                                                 return Result.err(Status.ERR_BadData, "Invalid Date Format for Entry");
2855                                         } catch(NumberFormatException e) {
2856                                                 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2857                                         }
2858                                 }
2859                                 isLastCred = (entry==-1)?true:false;
2860                         } else {
2861                                 isLastCred = true;
2862                         }
2863                         if (entry < -1 || entry >= rlcd.value.size()) {
2864                                 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2865                         }
2866                 }
2867                 
2868                 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from,cred.value,false, 
2869                         new Mapper.Memo() {
2870                                 @Override
2871                                 public String get() {
2872                                         return "Delete Credential [" + 
2873                                                 cred.value.id + 
2874                                                 ']';
2875                                 }
2876                         },
2877                         mc);
2878         
2879                 Result<List<NsDAO.Data>> nsr = ques.nsDAO.read(trans, cred.value.ns);
2880                 if(nsr.notOKorIsEmpty()) {
2881                         return Result.err(nsr);
2882                 }
2883         
2884                 switch(fd.status) {
2885                         case OK:
2886                                 Result<String> rfc = func.createFuture(trans, fd.value, cred.value.id,
2887                                                 trans.user(), nsr.value.get(0), FUTURE_OP.D);
2888         
2889                                 if(rfc.isOK()) {
2890                                         return Result.err(Status.ACC_Future, "Credential Delete [%s] is saved for future processing",cred.value.id);
2891                                 } else { 
2892                                         return Result.err(rfc);
2893                                 }
2894                         case Status.ACC_Now:
2895                                 Result<?>udr = null;
2896                                 if (!trans.requested(force)) {
2897                                         if(entry<0 || entry >= rlcd.value.size()) {
2898                                                 return Result.err(Status.ERR_BadData,"Invalid Choice [" + entry + "] chosen for Delete [%s] is saved for future processing",cred.value.id);
2899                                         }
2900                                         udr = ques.credDAO.delete(trans, rlcd.value.get(entry),false);
2901                                 } else {
2902                                         for (CredDAO.Data curr : rlcd.value) {
2903                                                 udr = ques.credDAO.delete(trans, curr, false);
2904                                                 if (udr.notOK()) {
2905                                                         return Result.err(udr);
2906                                                 }
2907                                         }
2908                                 }
2909                                 if(isLastCred) {
2910                                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, cred.value.id);
2911                                         if(rlurd.isOK()) {
2912                                                 for(UserRoleDAO.Data data : rlurd.value) {
2913                                                         ques.userRoleDAO.delete(trans, data, false);
2914                                                 }
2915                                         }
2916                                 }
2917                                 if(udr==null) {
2918                                         return Result.err(Result.ERR_NotFound,"No User Data found");
2919                                 }
2920                                 if (udr.isOK()) {
2921                                         return Result.ok();
2922                                 }
2923                                 return Result.err(udr);
2924                         default:
2925                                 return Result.err(fd);
2926                 }
2927         
2928         }
2929
2930
2931         @Override
2932         public Result<Date> doesCredentialMatch(AuthzTrans trans, REQUEST credReq) {
2933                 TimeTaken tt = trans.start("Does Credential Match", Env.SUB);
2934                 try {
2935                         // Note: Mapper assigns RAW type
2936                         Result<CredDAO.Data> data = mapper.cred(trans, credReq,false);
2937                         if(data.notOKorIsEmpty()) {
2938                                 return Result.err(data);
2939                         }
2940                         CredDAO.Data cred = data.value; // of the Mapped Cred
2941                         if(cred.cred==null) {
2942                                 return Result.err(Result.ERR_BadData,"No Password");
2943                         } else {
2944                                 return ques.doesUserCredMatch(trans, cred.id, cred.cred.array());
2945                         }
2946
2947                 } catch (DAOException e) {
2948                         trans.error().log(e,"Error looking up cred");
2949                         return Result.err(Status.ERR_Denied,"Credential does not match");
2950                 } finally {
2951                         tt.done();
2952                 }
2953         }
2954
2955         @ApiDoc( 
2956                         method = GET,  
2957                         path = "/authn/basicAuth",
2958                         params = {},
2959                         expectedCode = 200,
2960                         errorCodes = { 403 }, 
2961                         text = { "!!!! DEPRECATED without X509 Authentication STOP USING THIS API BY DECEMBER 2017, or use Certificates !!!!\n" 
2962                                         + "Use /authn/validate instead\n"
2963                                         + "Note: Validate a Password using BasicAuth Base64 encoded Header. This HTTP/S call is intended as a fast"
2964                                         + " User/Password lookup for Security Frameworks, and responds 200 if it passes BasicAuth "
2965                                 + "security, and 403 if it does not." }
2966                         )
2967         private void basicAuth() {
2968                 // This is a place holder for Documentation.  The real BasicAuth API does not call Service.
2969         }
2970         
2971         @ApiDoc( 
2972                         method = POST,  
2973                         path = "/authn/validate",
2974                         params = {},
2975                         expectedCode = 200,
2976                         errorCodes = { 403 }, 
2977                         text = { "Validate a Credential given a Credential Structure.  This is a more comprehensive validation, can "
2978                                         + "do more than BasicAuth as Credential types exp" }
2979                         )
2980         @Override
2981         public Result<Date> validateBasicAuth(AuthzTrans trans, String basicAuth) {
2982                 //TODO how to make sure people don't use this in browsers?  Do we care?
2983                 TimeTaken tt = trans.start("Validate Basic Auth", Env.SUB);
2984                 try {
2985                         BasicPrincipal bp = new BasicPrincipal(basicAuth,trans.org().getRealm());
2986                         Result<Date> rq = ques.doesUserCredMatch(trans, bp.getName(), bp.getCred());
2987                         // Note: Only want to log problem, don't want to send back to end user
2988                         if(rq.isOK()) {
2989                                 return rq;
2990                         } else {
2991                                 trans.audit().log(rq.errorString());
2992                         }
2993                 } catch (Exception e) {
2994                         trans.warn().log(e);
2995                 } finally {
2996                         tt.done();
2997                 }
2998                 return Result.err(Status.ERR_Denied,"Bad Basic Auth");
2999         }
3000
3001 /***********************************
3002  * USER-ROLE 
3003  ***********************************/
3004         @ApiDoc( 
3005                         method = POST,  
3006                         path = "/authz/userRole",
3007                         params = {},
3008                         expectedCode = 201,
3009                         errorCodes = {403,404,406,409}, 
3010                         text = { "Create a UserRole relationship (add User to Role)",
3011                                          "A UserRole is an object Representation of membership of a Role for limited time.",
3012                                          "If a shorter amount of time for Role ownership is required, use the 'End' field.",
3013                                          "** Note: Owners of Namespaces will be required to revalidate users in these roles ",
3014                                          "before Expirations expire.  Namespace owners will be notified by email."
3015                                    }
3016                         )
3017         @Override
3018         public Result<Void> createUserRole(final AuthzTrans trans, REQUEST from) {
3019                 TimeTaken tt = trans.start("Create UserRole", Env.SUB);
3020                 try {
3021                         Result<UserRoleDAO.Data> urr = mapper.userRole(trans, from);
3022                         if(urr.notOKorIsEmpty()) {
3023                                 return Result.err(urr);
3024                         }
3025                         final UserRoleDAO.Data userRole = urr.value;
3026                         
3027                         final ServiceValidator v = new ServiceValidator();
3028                         if(v.user_role(userRole).err() ||
3029                            v.user(trans.org(), userRole.user).err()) {
3030                                 return Result.err(Status.ERR_BadData,v.errs());
3031                         }
3032
3033
3034                          
3035                         // Check if user can change first
3036                         Result<FutureDAO.Data> fd = mapper.future(trans,UserRoleDAO.TABLE,from,urr.value,true, // may request Approvals
3037                                 new Mapper.Memo() {
3038                                         @Override
3039                                         public String get() {
3040                                                 return "Add User [" + userRole.user + "] to Role [" + 
3041                                                                 userRole.role + 
3042                                                                 ']';
3043                                         }
3044                                 },
3045                                 new MayChange() {
3046                                         private Result<NsDAO.Data> nsd;
3047                                         @Override
3048                                         public Result<?> mayChange() {
3049                                                 if(nsd==null) {
3050                                                         RoleDAO.Data r = RoleDAO.Data.decode(userRole);
3051                                                         nsd = ques.mayUser(trans, trans.user(), r, Access.write);
3052                                                 }
3053                                                 return nsd;
3054                                         }
3055                                 });
3056                         Result<NsDAO.Data> nsr = ques.deriveNs(trans, userRole.role);
3057                         if(nsr.notOKorIsEmpty()) {
3058                                 return Result.err(nsr);
3059                         }
3060
3061                         switch(fd.status) {
3062                                 case OK:
3063                                         Result<String> rfc = func.createFuture(trans, fd.value, userRole.user+'|'+userRole.ns + '.' + userRole.rname, 
3064                                                         userRole.user, nsr.value, FUTURE_OP.C);
3065                                         if(rfc.isOK()) {
3066                                                 return Result.err(Status.ACC_Future, "UserRole [%s - %s.%s] is saved for future processing",
3067                                                                 userRole.user,
3068                                                                 userRole.ns,
3069                                                                 userRole.rname);
3070                                         } else { 
3071                                                 return Result.err(rfc);
3072                                         }
3073                                 case Status.ACC_Now:
3074                                         return func.addUserRole(trans, userRole);
3075                                 default:
3076                                         return Result.err(fd);
3077                         }
3078                 } finally {
3079                         tt.done();
3080                 }
3081         }
3082         
3083                 /**
3084                  * getUserRolesByRole
3085                  */
3086             @ApiDoc(
3087                     method = GET,
3088                     path = "/authz/userRoles/role/:role",
3089                     params = {"role|string|true"},
3090                     expectedCode = 200,
3091                     errorCodes = {404,406},
3092                     text = { "List all Users that are attached to Role specified in :role",
3093                                 }
3094                    )
3095                 @Override
3096                 public Result<USERROLES> getUserRolesByRole(AuthzTrans trans, String role) {
3097                         final Validator v = new ServiceValidator();
3098                         if(v.nullOrBlank("Role",role).err()) {
3099                                 return Result.err(Status.ERR_BadData,v.errs());
3100                         }
3101                         
3102                         Result<RoleDAO.Data> rrdd;
3103                         rrdd = RoleDAO.Data.decode(trans,ques,role);
3104                         if(rrdd.notOK()) {
3105                                 return Result.err(rrdd);
3106                         }
3107                         // May Requester see result?
3108                         Result<NsDAO.Data> ns = ques.mayUser(trans,trans.user(), rrdd.value,Access.read);
3109                         if (ns.notOK()) {
3110                                 return Result.err(ns);
3111                         }
3112         
3113         //              boolean filter = true;          
3114         //              if (ns.value.isAdmin(trans.user()) || ns.value.isResponsible(trans.user()))
3115         //                      filter = false;
3116                         
3117                         // Get list of roles per user, then add to Roles as we go
3118                         HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3119                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3120                         if(rlurd.isOK()) {
3121                                 for(UserRoleDAO.Data data : rlurd.value) {
3122                                         userSet.add(data);
3123                                 }
3124                         }
3125                         
3126                         @SuppressWarnings("unchecked")
3127                         USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3128                         // Checked for permission
3129                         mapper.userRoles(trans, userSet, users);
3130                         return Result.ok(users);
3131                 }
3132                 /**
3133                  * getUserRolesByRole
3134                  */
3135             @ApiDoc(
3136                     method = GET,
3137                     path = "/authz/userRoles/user/:user",
3138                     params = {"role|string|true"},
3139                     expectedCode = 200,
3140                     errorCodes = {404,406},
3141                     text = { "List all UserRoles for :user",
3142                                 }
3143                    )
3144                 @Override
3145                 public Result<USERROLES> getUserRolesByUser(AuthzTrans trans, String user) {
3146                         final Validator v = new ServiceValidator();
3147                         if(v.nullOrBlank("User",user).err()) {
3148                                 return Result.err(Status.ERR_BadData,v.errs());
3149                         }
3150                         
3151                         // Get list of roles per user, then add to Roles as we go
3152                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, user);
3153                         if(rlurd.notOK()) { 
3154                                 return Result.err(rlurd);
3155                         }
3156                         
3157                         /* Check for
3158                          *   1) is User 
3159                          *   2) is User's Supervisor
3160                          *   3) Has special global access =read permission
3161                          *   
3162                          *   If none of the 3, then filter results to NSs in which Calling User has Ns.access * read
3163                          */
3164                         boolean mustFilter;
3165                         String callingUser = trans.getUserPrincipal().getName();
3166                         NsDAO.Data ndd = new NsDAO.Data();
3167
3168                         if(user.equals(callingUser)) {
3169                                 mustFilter = false;
3170                         } else {
3171                                 Organization org = trans.org();
3172                                 try {
3173                                         Identity orgID = org.getIdentity(trans, user);
3174                                         Identity manager = orgID==null?null:orgID.responsibleTo();
3175                                         if(orgID!=null && (manager!=null && callingUser.equals(manager.fullID()))) {
3176                                                 mustFilter = false;
3177                                         } else if(ques.isGranted(trans, callingUser, ROOT_NS, Question.ACCESS, "*", Access.read.name())) {
3178                                                 mustFilter=false;
3179                                         } else {
3180                                                 mustFilter = true;
3181                                         }
3182                                 } catch (OrganizationException e) {
3183                                         trans.env().log(e);
3184                                         mustFilter = true;
3185                                 }
3186                         }
3187                         
3188                         List<UserRoleDAO.Data> content;
3189                         if(mustFilter) {
3190                                 content = new ArrayList<UserRoleDAO.Data>(rlurd.value.size()); // avoid multi-memory redos
3191                                 
3192                                 for(UserRoleDAO.Data data : rlurd.value) {
3193                                         ndd.name=data.ns;
3194                                         Result<Data> mur = ques.mayUser(trans, callingUser, ndd, Access.read);
3195                                         if(mur.isOK()){
3196                                                 content.add(data);
3197                                         }
3198                                 }
3199                                 
3200                         } else {
3201                                 content = rlurd.value;
3202                         }
3203
3204
3205                         @SuppressWarnings("unchecked")
3206                         USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3207                         // Checked for permission
3208                         mapper.userRoles(trans, content, users);
3209                         return Result.ok(users);
3210                 }
3211
3212             
3213         @ApiDoc( 
3214                         method = PUT,  
3215                         path = "/authz/userRole/user",
3216                         params = {},
3217                         expectedCode = 200,
3218                         errorCodes = {403,404,406}, 
3219                         text = { "Set a User's roles to the roles specified in the UserRoleRequest object.",
3220                                                 "WARNING: Roles supplied will be the ONLY roles attached to this user",
3221                                                 "If no roles are supplied, user's roles are reset."
3222                                    }
3223                         )
3224         @Override
3225         public Result<Void> resetRolesForUser(AuthzTrans trans, REQUEST rreq) {
3226                 Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3227                 final ServiceValidator v = new ServiceValidator();
3228                 if(rurdd.notOKorIsEmpty()) {
3229                         return Result.err(rurdd);
3230                 }
3231                 if (v.user(trans.org(), rurdd.value.user).err()) {
3232                         return Result.err(Status.ERR_BadData,v.errs());
3233                 }
3234
3235                 Set<String> currRoles = new HashSet<String>();
3236                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByUser(trans, rurdd.value.user);
3237                 if(rlurd.isOK()) {
3238                         for(UserRoleDAO.Data data : rlurd.value) {
3239                                 currRoles.add(data.role);
3240                         }
3241                 }
3242                 
3243                 Result<Void> rv = null;
3244                 String[] roles;
3245                 if(rurdd.value.role==null) {
3246                         roles = new String[0];
3247                 } else {
3248                         roles = rurdd.value.role.split(",");
3249                 }
3250                 
3251                 for (String role : roles) {                     
3252                         if (v.role(role).err()) {
3253                                 return Result.err(Status.ERR_BadData,v.errs());
3254                         }
3255                         Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3256                         if(rrdd.notOK()) {
3257                                 return Result.err(rrdd);
3258                         }
3259                         
3260                         rurdd.value.role(rrdd.value);
3261                         
3262                         Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rrdd.value,Access.write);
3263                         if (nsd.notOK()) {
3264                                 return Result.err(nsd);
3265                         }
3266                         Result<NsDAO.Data> nsr = ques.deriveNs(trans, role);
3267                         if(nsr.notOKorIsEmpty()) {
3268                                 return Result.err(nsr); 
3269                         }
3270                         
3271                         if(currRoles.contains(role)) {
3272                                 currRoles.remove(role);
3273                         } else {
3274                                 rv = func.addUserRole(trans, rurdd.value);
3275                                 if (rv.notOK()) {
3276                                         return rv;
3277                                 }
3278                         }
3279                 }
3280                 
3281                 for (String role : currRoles) {
3282                         rurdd.value.role(trans,ques,role);
3283                         rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3284                         if(rv.notOK()) {
3285                                 trans.info().log(rurdd.value.user,"/",rurdd.value.role, "expected to be deleted, but does not exist");
3286                                 // return rv; // if it doesn't exist, don't error out
3287                         }
3288
3289                 }
3290         
3291                 return Result.ok();             
3292                 
3293         }
3294         
3295         @ApiDoc( 
3296                         method = PUT,  
3297                         path = "/authz/userRole/role",
3298                         params = {},
3299                         expectedCode = 200,
3300                         errorCodes = {403,404,406}, 
3301                         text = { "Set a Role's users to the users specified in the UserRoleRequest object.",
3302                                         "WARNING: Users supplied will be the ONLY users attached to this role",
3303                                         "If no users are supplied, role's users are reset."
3304                            }
3305                         )
3306         @Override
3307         public Result<Void> resetUsersForRole(AuthzTrans trans, REQUEST rreq) {
3308                 Result<UserRoleDAO.Data> rurdd = mapper.userRole(trans, rreq);
3309                 if(rurdd.notOKorIsEmpty()) {
3310                         return Result.err(rurdd);
3311                 }
3312                 final ServiceValidator v = new ServiceValidator();
3313                 if (v.user_role(rurdd.value).err()) {
3314                         return Result.err(Status.ERR_BadData,v.errs());
3315                 }
3316
3317                 RoleDAO.Data rd = RoleDAO.Data.decode(rurdd.value);
3318
3319                 Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), rd, Access.write);
3320                 if (nsd.notOK()) {
3321                         return Result.err(nsd);
3322                 }
3323
3324                 Result<NsDAO.Data> nsr = ques.deriveNs(trans, rurdd.value.role);
3325                 if(nsr.notOKorIsEmpty()) {
3326                         return Result.err(nsr); 
3327                 }
3328
3329                 Set<String> currUsers = new HashSet<String>();
3330                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, rurdd.value.role);
3331                 if(rlurd.isOK()) { 
3332                         for(UserRoleDAO.Data data : rlurd.value) {
3333                                 currUsers.add(data.user);
3334                         }
3335                 }
3336         
3337                 // found when connected remotely to DEVL, can't replicate locally
3338                 // inconsistent errors with cmd: role user setTo [nothing]
3339                 // deleteUserRole --> read --> get --> cacheIdx(?)
3340                 // sometimes returns idx for last added user instead of user passed in
3341                 // cache bug? 
3342                 
3343                 
3344                 Result<Void> rv = null;
3345                 String[] users = {};
3346                 if (rurdd.value.user != null) {
3347                     users = rurdd.value.user.split(",");
3348                 }
3349                 
3350                 for (String user : users) {                     
3351                         if (v.user(trans.org(), user).err()) {
3352                                 return Result.err(Status.ERR_BadData,v.errs());
3353                         }
3354                         rurdd.value.user = user;
3355
3356                         if(currUsers.contains(user)) {
3357                                 currUsers.remove(user);
3358                         } else {
3359                                 rv = func.addUserRole(trans, rurdd.value);
3360                                 if (rv.notOK()) { 
3361                                         return rv;
3362                                 }
3363                         }
3364                 }
3365                 
3366                 for (String user : currUsers) {
3367                         rurdd.value.user = user; 
3368                         rv = ques.userRoleDAO.delete(trans, rurdd.value, false);
3369                         if(rv.notOK()) {
3370                                 trans.info().log(rurdd.value, "expected to be deleted, but not exists");
3371                                 return rv;
3372                         }
3373                 }       
3374                 
3375                 return Result.ok();                     
3376         }
3377         
3378         @ApiDoc(
3379                 method = GET,
3380                 path = "/authz/userRole/extend/:user/:role",
3381                 params = {      "user|string|true",
3382                                         "role|string|true"
3383                                 },
3384                 expectedCode = 200,
3385                 errorCodes = {403,404,406},
3386                 text = { "Extend the Expiration of this User Role by the amount set by Organization",
3387                                  "Requestor must be allowed to modify the role"
3388                                 }
3389                )
3390         @Override
3391         public Result<Void> extendUserRole(AuthzTrans trans, String user, String role) {
3392                 Organization org = trans.org();
3393                 final ServiceValidator v = new ServiceValidator();
3394                 if(v.user(org, user)
3395                         .role(role)
3396                         .err()) {
3397                         return Result.err(Status.ERR_BadData,v.errs());
3398                 }
3399         
3400                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3401                 if(rrdd.notOK()) {
3402                         return Result.err(rrdd);
3403                 }
3404                 
3405                 Result<NsDAO.Data> rcr = ques.mayUser(trans, trans.user(), rrdd.value, Access.write);
3406                 boolean mayNotChange;
3407                 if((mayNotChange = rcr.notOK()) && !trans.requested(future)) {
3408                         return Result.err(rcr);
3409                 }
3410                 
3411                 Result<List<UserRoleDAO.Data>> rr = ques.userRoleDAO.read(trans, user,role);
3412                 if(rr.notOK()) {
3413                         return Result.err(rr);
3414                 }
3415                 for(UserRoleDAO.Data userRole : rr.value) {
3416                         if(mayNotChange) { // Function exited earlier if !trans.futureRequested
3417                                 FutureDAO.Data fto = new FutureDAO.Data();
3418                                 fto.target=UserRoleDAO.TABLE;
3419                                 fto.memo = "Extend User ["+userRole.user+"] in Role ["+userRole.role+"]";
3420                                 GregorianCalendar now = new GregorianCalendar();
3421                                 fto.start = now.getTime();
3422                                 fto.expires = org.expiration(now, Expiration.Future).getTime();
3423                                 try {
3424                                         fto.construct = userRole.bytify();
3425                                 } catch (IOException e) {
3426                                         trans.error().log(e, "Error while bytifying UserRole for Future");
3427                                         return Result.err(e);
3428                                 }
3429
3430                                 Result<String> rfc = func.createFuture(trans, fto, 
3431                                                 userRole.user+'|'+userRole.role, userRole.user, rcr.value, FUTURE_OP.U);
3432                                 if(rfc.isOK()) {
3433                                         return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3434                                                         userRole.user,
3435                                                         userRole.role);
3436                                 } else {
3437                                         return Result.err(rfc);
3438                                 }
3439                         } else {
3440                                 return func.extendUserRole(trans, userRole, false);
3441                         }
3442                 }
3443                 return Result.err(Result.ERR_NotFound,"This user and role doesn't exist");
3444         }
3445
3446         @ApiDoc( 
3447                         method = DELETE,  
3448                         path = "/authz/userRole/:user/:role",
3449                         params = {      "user|string|true",
3450                                                 "role|string|true"
3451                                         },
3452                         expectedCode = 200,
3453                         errorCodes = {403,404,406}, 
3454                         text = { "Remove Role :role from User :user."
3455                                    }
3456                         )
3457         @Override
3458         public Result<Void> deleteUserRole(AuthzTrans trans, String usr, String role) {
3459                 Validator val = new ServiceValidator();
3460                 if(val.nullOrBlank("User", usr)
3461                       .nullOrBlank("Role", role).err()) {
3462                         return Result.err(Status.ERR_BadData, val.errs());
3463                 }
3464
3465                 boolean mayNotChange;
3466                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3467                 if(rrdd.notOK()) {
3468                         return Result.err(rrdd);
3469                 }
3470                 
3471                 RoleDAO.Data rdd = rrdd.value;
3472                 Result<NsDAO.Data> rns = ques.mayUser(trans, trans.user(), rdd, Access.write);
3473
3474                 // Make sure we don't delete the last owner of valid NS
3475                 if(rns.isOKhasData() && Question.OWNER.equals(rdd.name) && ques.countOwner(trans,rdd.ns)<=1) {
3476                         return Result.err(Status.ERR_Denied,"You may not delete the last Owner of " + rdd.ns );
3477                 }
3478                 
3479                 if(mayNotChange=rns.notOK()) {
3480                         if(!trans.requested(future)) {
3481                                 return Result.err(rns);
3482                         }
3483                 }
3484
3485                 Result<List<UserRoleDAO.Data>> rulr;
3486                 if((rulr=ques.userRoleDAO.read(trans, usr, role)).notOKorIsEmpty()) {
3487                         return Result.err(Status.ERR_UserRoleNotFound, "User [ "+usr+" ] is not "
3488                                         + "Assigned to the Role [ " + role + " ]");
3489                 }
3490
3491                 UserRoleDAO.Data userRole = rulr.value.get(0);
3492                 if(mayNotChange) { // Function exited earlier if !trans.futureRequested
3493                         FutureDAO.Data fto = new FutureDAO.Data();
3494                         fto.target=UserRoleDAO.TABLE;
3495                         fto.memo = "Remove User ["+userRole.user+"] from Role ["+userRole.role+"]";
3496                         GregorianCalendar now = new GregorianCalendar();
3497                         fto.start = now.getTime();
3498                         fto.expires = trans.org().expiration(now, Expiration.Future).getTime();
3499
3500                         Result<String> rfc = func.createFuture(trans, fto, 
3501                                         userRole.user+'|'+userRole.role, userRole.user, rns.value, FUTURE_OP.D);
3502                         if(rfc.isOK()) {
3503                                 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing", 
3504                                                 userRole.user,
3505                                                 userRole.role);
3506                         } else { 
3507                                 return Result.err(rfc);
3508                         }
3509                 } else {
3510                         return ques.userRoleDAO.delete(trans, rulr.value.get(0), false);
3511                 }
3512         }
3513
3514         @ApiDoc( 
3515                         method = GET,  
3516                         path = "/authz/userRole/:user/:role",
3517                         params = {"user|string|true",
3518                                           "role|string|true"},
3519                         expectedCode = 200,
3520                         errorCodes = {403,404,406}, 
3521                         text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3522                                    }
3523                         )
3524         @Override
3525         public Result<USERS> getUserInRole(AuthzTrans trans, String user, String role) {
3526                 final Validator v = new ServiceValidator();
3527                 if(v.role(role).nullOrBlank("User", user).err()) {
3528                         return Result.err(Status.ERR_BadData,v.errs());
3529                 }
3530
3531 //              Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3532 //              if (ns.notOK()) return Result.err(ns);
3533 //              
3534 //              Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3535                 // May calling user see by virtue of the Role
3536                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3537                 if(rrdd.notOK()) {
3538                         return Result.err(rrdd);
3539                 }
3540                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3541                 if(rnd.notOK()) {
3542                         return Result.err(rnd); 
3543                 }
3544                 
3545                 HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3546                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readUserInRole(trans, user, role);
3547                 if(rlurd.isOK()) {
3548                         for(UserRoleDAO.Data data : rlurd.value) {
3549                                 userSet.add(data);
3550                         }
3551                 }
3552                 
3553                 @SuppressWarnings("unchecked")
3554                 USERS users = (USERS) mapper.newInstance(API.USERS);
3555                 mapper.users(trans, userSet, users);
3556                 return Result.ok(users);
3557         }
3558
3559         @ApiDoc( 
3560                         method = GET,  
3561                         path = "/authz/users/role/:role",
3562                         params = {"user|string|true",
3563                                           "role|string|true"},
3564                         expectedCode = 200,
3565                         errorCodes = {403,404,406}, 
3566                         text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3567                                    }
3568                         )
3569         @Override
3570         public Result<USERS> getUsersByRole(AuthzTrans trans, String role) {
3571                 final Validator v = new ServiceValidator();
3572                 if(v.nullOrBlank("Role",role).err()) {
3573                         return Result.err(Status.ERR_BadData,v.errs());
3574                 }
3575
3576 //              Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3577 //              if (ns.notOK()) return Result.err(ns);
3578 //              
3579 //              Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3580                 // May calling user see by virtue of the Role
3581                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3582                 if(rrdd.notOK()) {
3583                         return Result.err(rrdd);
3584                 }
3585                 
3586                 boolean contactOnly = false;
3587                 // Allow the request of any valid user to find the contact of the NS (Owner)
3588                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3589                 if(rnd.notOK()) {
3590                         if(Question.OWNER.equals(rrdd.value.name)) {
3591                                 contactOnly = true;
3592                         } else {
3593                                 return Result.err(rnd);
3594                         }
3595                 }
3596                 
3597                 HashSet<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3598                 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role);
3599                 if(rlurd.isOK()) { 
3600                         for(UserRoleDAO.Data data : rlurd.value) {
3601                                 if(contactOnly) { //scrub data
3602                                         // Can't change actual object, or will mess up the cache.
3603                                         UserRoleDAO.Data scrub = new UserRoleDAO.Data();
3604                                         scrub.ns = data.ns;
3605                                         scrub.rname = data.rname;
3606                                         scrub.role = data.role;
3607                                         scrub.user = data.user;
3608                                         userSet.add(scrub);
3609                                 } else {
3610                                         userSet.add(data);
3611                                 }
3612                         }
3613                 }
3614                 
3615                 @SuppressWarnings("unchecked")
3616                 USERS users = (USERS) mapper.newInstance(API.USERS);
3617                 mapper.users(trans, userSet, users);
3618                 return Result.ok(users);
3619         }
3620
3621         /**
3622          * getUsersByPermission
3623          */
3624     @ApiDoc(
3625             method = GET,
3626             path = "/authz/users/perm/:type/:instance/:action",
3627             params = {  "type|string|true",
3628                                 "instance|string|true",
3629                                 "action|string|true"
3630                         },
3631             expectedCode = 200,
3632             errorCodes = {404,406},
3633             text = { "List all Users that have Permission specified by :type :instance :action",
3634                         }
3635            )
3636         @Override
3637         public Result<USERS> getUsersByPermission(AuthzTrans trans, String type, String instance, String action) {
3638                 final Validator v = new ServiceValidator();
3639                 if(v.nullOrBlank("Type",type)
3640                         .nullOrBlank("Instance",instance)
3641                         .nullOrBlank("Action",action)                   
3642                         .err()) {
3643                         return Result.err(Status.ERR_BadData,v.errs());
3644                 }
3645
3646                 Result<NsSplit> nss = ques.deriveNsSplit(trans, type);
3647                 if(nss.notOK()) {
3648                         return Result.err(nss);
3649                 }
3650                 
3651                 Result<List<NsDAO.Data>> nsd = ques.nsDAO.read(trans, nss.value.ns);
3652                 if (nsd.notOK()) {
3653                         return Result.err(nsd);
3654                 }
3655                 
3656                 boolean allInstance = ASTERIX.equals(instance);
3657                 boolean allAction = ASTERIX.equals(action);
3658                 // Get list of roles per Permission, 
3659                 // Then loop through Roles to get Users
3660                 // Note: Use Sets to avoid processing or responding with Duplicates
3661                 Set<String> roleUsed = new HashSet<String>();
3662                 Set<UserRoleDAO.Data> userSet = new HashSet<UserRoleDAO.Data>();
3663                 
3664                 if(!nss.isEmpty()) {
3665                         Result<List<PermDAO.Data>> rlp = ques.permDAO.readByType(trans, nss.value.ns, nss.value.name);
3666                         if(rlp.isOKhasData()) {
3667                                 for(PermDAO.Data pd : rlp.value) {
3668                                         if((allInstance || pd.instance.equals(instance)) && 
3669                                                         (allAction || pd.action.equals(action))) {
3670                                                 if(ques.mayUser(trans, trans.user(),pd,Access.read).isOK()) {
3671                                                         for(String role : pd.roles) {
3672                                                                 if(!roleUsed.contains(role)) { // avoid evaluating Role many times
3673                                                                         roleUsed.add(role);
3674                                                                         Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO.readByRole(trans, role.replace('|', '.'));
3675                                                                         if(rlurd.isOKhasData()) {
3676                                                                             for(UserRoleDAO.Data urd : rlurd.value) {
3677                                                                                 userSet.add(urd);
3678                                                                             }
3679                                                                         }
3680                                                                 }
3681                                                         }
3682                                                 }
3683                                         }
3684                                 }
3685                         }
3686                 }
3687                 @SuppressWarnings("unchecked")
3688                 USERS users = (USERS) mapper.newInstance(API.USERS);
3689                 mapper.users(trans, userSet, users);
3690                 return Result.ok(users);
3691         }
3692
3693     /***********************************
3694  * HISTORY 
3695  ***********************************/   
3696         @Override
3697         public Result<HISTORY> getHistoryByUser(final AuthzTrans trans, String user, final int[] yyyymm, final int sort) {      
3698                 final Validator v = new ServiceValidator();
3699                 if(v.nullOrBlank("User",user).err()) {
3700                         return Result.err(Status.ERR_BadData,v.errs());
3701                 }
3702
3703                 Result<NsDAO.Data> rnd;
3704                 // Users may look at their own data
3705                  if(trans.user().equals(user)) {
3706                                 // Users may look at their own data
3707                  } else {
3708                         int at = user.indexOf('@');
3709                         if(at>=0 && trans.org().getRealm().equals(user.substring(at+1))) {
3710                                 NsDAO.Data nsd  = new NsDAO.Data();
3711                                 nsd.name = Question.domain2ns(user);
3712                                 rnd = ques.mayUser(trans, trans.user(), nsd, Access.read);
3713                                 if(rnd.notOK()) {
3714                                         return Result.err(rnd);
3715                                 }
3716                         } else {
3717                                 rnd = ques.validNSOfDomain(trans, user);
3718                                 if(rnd.notOK()) {
3719                                         return Result.err(rnd);
3720                                 }
3721
3722                                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3723                                 if(rnd.notOK()) {
3724                                         return Result.err(rnd);
3725                                 }
3726                         }
3727                  }
3728                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readByUser(trans, user, yyyymm);
3729                 if(resp.notOK()) {
3730                         return Result.err(resp);
3731                 }
3732                 return mapper.history(trans, resp.value,sort);
3733         }
3734
3735         @Override
3736         public Result<HISTORY> getHistoryByRole(AuthzTrans trans, String role, int[] yyyymm, final int sort) {
3737                 final Validator v = new ServiceValidator();
3738                 if(v.nullOrBlank("Role",role).err()) {
3739                         return Result.err(Status.ERR_BadData,v.errs());
3740                 }
3741
3742                 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3743                 if(rrdd.notOK()) {
3744                         return Result.err(rrdd);
3745                 }
3746                 
3747                 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
3748                 if(rnd.notOK()) {
3749                         return Result.err(rnd);
3750                 }
3751                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, role, "role", yyyymm); 
3752                 if(resp.notOK()) {
3753                         return Result.err(resp);
3754                 }
3755                 return mapper.history(trans, resp.value,sort);
3756         }
3757
3758         @Override
3759         public Result<HISTORY> getHistoryByPerm(AuthzTrans trans, String type, int[] yyyymm, final int sort) {
3760                 final Validator v = new ServiceValidator();
3761                 if(v.nullOrBlank("Type",type)
3762                         .err()) {
3763                         return Result.err(Status.ERR_BadData,v.errs());
3764                 }
3765
3766                 // May user see Namespace of Permission (since it's only one piece... we can't check for "is permission part of")
3767                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,type);
3768                 if(rnd.notOK()) {
3769                         return Result.err(rnd);
3770                 }
3771                 
3772                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3773                 if(rnd.notOK()) {
3774                         return Result.err(rnd); 
3775                 }
3776                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, type, "perm", yyyymm);
3777                 if(resp.notOK()) {
3778                         return Result.err(resp);
3779                 }
3780                 return mapper.history(trans, resp.value,sort);
3781         }
3782
3783         @Override
3784         public Result<HISTORY> getHistoryByNS(AuthzTrans trans, String ns, int[] yyyymm, final int sort) {
3785                 final Validator v = new ServiceValidator();
3786                 if(v.nullOrBlank("NS",ns)
3787                         .err()) { 
3788                         return Result.err(Status.ERR_BadData,v.errs());
3789                 }
3790
3791                 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
3792                 if(rnd.notOK()) {
3793                         return Result.err(rnd);
3794                 }
3795                 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3796                 if(rnd.notOK()) {
3797                         return Result.err(rnd); 
3798                 }
3799
3800                 Result<List<HistoryDAO.Data>> resp = ques.historyDAO.readBySubject(trans, ns, "ns", yyyymm);
3801                 if(resp.notOK()) {
3802                         return Result.err(resp);
3803                 }
3804                 return mapper.history(trans, resp.value,sort);
3805         }
3806
3807 /***********************************
3808  * DELEGATE 
3809  ***********************************/
3810         @Override
3811         public Result<Void> createDelegate(final AuthzTrans trans, REQUEST base) {
3812                 return createOrUpdateDelegate(trans, base, Question.Access.create);
3813         }
3814
3815         @Override
3816         public Result<Void> updateDelegate(AuthzTrans trans, REQUEST base) {
3817                 return createOrUpdateDelegate(trans, base, Question.Access.write);
3818         }
3819
3820
3821         private Result<Void> createOrUpdateDelegate(final AuthzTrans trans, REQUEST base, final Access access) {
3822                 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3823                 final ServiceValidator v = new ServiceValidator();
3824                 if(v.delegate(trans.org(),rd).err()) { 
3825                         return Result.err(Status.ERR_BadData,v.errs());
3826                 }
3827
3828                 final DelegateDAO.Data dd = rd.value;
3829                 
3830                 Result<List<DelegateDAO.Data>> ddr = ques.delegateDAO.read(trans, dd);
3831                 if(access==Access.create && ddr.isOKhasData()) {
3832                         return Result.err(Status.ERR_ConflictAlreadyExists, "[%s] already delegates to [%s]", dd.user, ddr.value.get(0).delegate);
3833                 } else if(access!=Access.create && ddr.notOKorIsEmpty()) { 
3834                         return Result.err(Status.ERR_NotFound, "[%s] does not have a Delegate Record to [%s].",dd.user,access.name());
3835                 }
3836                 Result<Void> rv = ques.mayUser(trans, dd, access);
3837                 if(rv.notOK()) {
3838                         return rv;
3839                 }
3840                 
3841                 Result<FutureDAO.Data> fd = mapper.future(trans,DelegateDAO.TABLE,base, dd, false, 
3842                         new Mapper.Memo() {
3843                                 @Override
3844                                 public String get() {
3845                                         StringBuilder sb = new StringBuilder();
3846                                         sb.append(access.name());
3847                                         sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
3848                                         sb.append("Delegate ");
3849                                         sb.append(access==Access.create?"[":"to [");
3850                                         sb.append(rd.value.delegate);
3851                                         sb.append("] for [");
3852                                         sb.append(rd.value.user);
3853                                         sb.append(']');
3854                                         return sb.toString();
3855                                 }
3856                         },
3857                         new MayChange() {
3858                                 @Override
3859                                 public Result<?> mayChange() {
3860                                         return Result.ok(); // Validate in code above
3861                                 }
3862                         });
3863                 
3864                 switch(fd.status) {
3865                         case OK:
3866                                 Result<String> rfc = func.createFuture(trans, fd.value, 
3867                                                 dd.user, trans.user(),null, access==Access.create?FUTURE_OP.C:FUTURE_OP.U);
3868                                 if(rfc.isOK()) { 
3869                                         return Result.err(Status.ACC_Future, "Delegate for [%s]",
3870                                                         dd.user);
3871                                 } else { 
3872                                         return Result.err(rfc);
3873                                 }
3874                         case Status.ACC_Now:
3875                                 if(access==Access.create) {
3876                                         Result<DelegateDAO.Data> rdr = ques.delegateDAO.create(trans, dd);
3877                                         if(rdr.isOK()) {
3878                                                 return Result.ok();
3879                                         } else {
3880                                                 return Result.err(rdr);
3881                                         }
3882                                 } else {
3883                                         return ques.delegateDAO.update(trans, dd);
3884                                 }
3885                         default:
3886                                 return Result.err(fd);
3887                 }
3888         }
3889
3890         @Override
3891         public Result<Void> deleteDelegate(AuthzTrans trans, REQUEST base) {
3892                 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3893                 final Validator v = new ServiceValidator();
3894                 if(v.notOK(rd).nullOrBlank("User", rd.value.user).err()) {
3895                         return Result.err(Status.ERR_BadData,v.errs());
3896                 }
3897                 
3898                 Result<List<DelegateDAO.Data>> ddl;
3899                 if((ddl=ques.delegateDAO.read(trans, rd.value)).notOKorIsEmpty()) {
3900                         return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3901                 }
3902                 final DelegateDAO.Data dd = ddl.value.get(0);
3903                 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3904                 if(rv.notOK()) {
3905                         return rv;
3906                 }
3907                 
3908                 return ques.delegateDAO.delete(trans, dd, false);
3909         }
3910
3911         @Override
3912         public Result<Void> deleteDelegate(AuthzTrans trans, String userName) {
3913                 DelegateDAO.Data dd = new DelegateDAO.Data();
3914                 final Validator v = new ServiceValidator();
3915                 if(v.nullOrBlank("User", userName).err()) {
3916                         return Result.err(Status.ERR_BadData,v.errs());
3917                 }
3918                 dd.user = userName;
3919                 Result<List<DelegateDAO.Data>> ddl;
3920                 if((ddl=ques.delegateDAO.read(trans, dd)).notOKorIsEmpty()) {
3921                         return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3922                 }
3923                 dd = ddl.value.get(0);
3924                 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3925                 if(rv.notOK()) {
3926                         return rv;
3927                 }
3928                 
3929                 return ques.delegateDAO.delete(trans, dd, false);
3930         }
3931         
3932         @Override
3933         public Result<DELGS> getDelegatesByUser(AuthzTrans trans, String user) {
3934                 final Validator v = new ServiceValidator();
3935                 if(v.nullOrBlank("User", user).err()) {
3936                         return Result.err(Status.ERR_BadData,v.errs());
3937                 }
3938
3939                 DelegateDAO.Data ddd = new DelegateDAO.Data();
3940                 ddd.user = user;
3941                 ddd.delegate = null;
3942                 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3943                 if(rv.notOK()) {
3944                         return Result.err(rv);
3945                 }
3946                 
3947                 TimeTaken tt = trans.start("Get delegates for a user", Env.SUB);
3948
3949                 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.read(trans, user);
3950                 try {
3951                         if (dbDelgs.isOKhasData()) {
3952                                 return mapper.delegate(dbDelgs.value);
3953                         } else {
3954                                 return Result.err(Status.ERR_DelegateNotFound,"No Delegate found for [%s]",user);
3955                         }
3956                 } finally {
3957                         tt.done();
3958                 }               
3959         }
3960
3961         @Override
3962         public Result<DELGS> getDelegatesByDelegate(AuthzTrans trans, String delegate) {
3963                 final Validator v = new ServiceValidator();
3964                 if(v.nullOrBlank("Delegate", delegate).err()) {
3965                         return Result.err(Status.ERR_BadData,v.errs());
3966                 }
3967
3968                 DelegateDAO.Data ddd = new DelegateDAO.Data();
3969                 ddd.user = delegate;
3970                 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3971                 if(rv.notOK()) {
3972                         return Result.err(rv);
3973                 }
3974
3975                 TimeTaken tt = trans.start("Get users for a delegate", Env.SUB);
3976
3977                 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO.readByDelegate(trans, delegate);
3978                 try {
3979                         if (dbDelgs.isOKhasData()) {
3980                                 return mapper.delegate(dbDelgs.value);
3981                         } else {
3982                                 return Result.err(Status.ERR_DelegateNotFound,"Delegate [%s] is not delegating for anyone.",delegate);
3983                         }
3984                 } finally {
3985                         tt.done();
3986                 }               
3987         }
3988
3989 /***********************************
3990  * APPROVAL 
3991  ***********************************/
3992         private static final String APPR_FMT = "actor=%s, action=%s, operation=\"%s\", requestor=%s, delegator=%s";
3993         @Override
3994         public Result<Void> updateApproval(AuthzTrans trans, APPROVALS approvals) {
3995                 Result<List<ApprovalDAO.Data>> rlad = mapper.approvals(approvals);
3996                 if(rlad.notOK()) {
3997                         return Result.err(rlad);
3998                 }
3999                 int numApprs = rlad.value.size();
4000                 if(numApprs<1) {
4001                         return Result.err(Status.ERR_NoApprovals,"No Approvals sent for Updating");
4002                 }
4003                 int numProcessed = 0;
4004                 String user = trans.user();
4005                 
4006                 Result<List<ApprovalDAO.Data>> curr;
4007                 Lookup<List<ApprovalDAO.Data>> apprByTicket=null;
4008                 for(ApprovalDAO.Data updt : rlad.value) {
4009                         if(updt.ticket!=null) {
4010                                 curr = ques.approvalDAO.readByTicket(trans, updt.ticket);
4011                                 if(curr.isOKhasData()) {
4012                                         final List<ApprovalDAO.Data> add = curr.value;
4013                                         apprByTicket = new Lookup<List<ApprovalDAO.Data>>() { // Store a Pre-Lookup
4014                                                 @Override
4015                                                 public List<ApprovalDAO.Data> get(AuthzTrans trans, Object ... noop) {
4016                                                         return add;
4017                                                 }
4018                                         };
4019                                 }
4020                         } else if(updt.id!=null) {
4021                                 curr = ques.approvalDAO.read(trans, updt);
4022                         } else if(updt.approver!=null) {
4023                                 curr = ques.approvalDAO.readByApprover(trans, updt.approver);
4024                         } else {
4025                                 return Result.err(Status.ERR_BadData,"Approvals need ID, Ticket or Approval data to update");
4026                         }
4027
4028                         if(curr.isOKhasData()) {
4029                         Map<String, Result<List<DelegateDAO.Data>>> delegateCache = new HashMap<String, Result<List<DelegateDAO.Data>>>();
4030                         Map<UUID, FutureDAO.Data> futureCache = new HashMap<UUID, FutureDAO.Data>();
4031                         FutureDAO.Data hasDeleted = new FutureDAO.Data();
4032                         
4033                             for(ApprovalDAO.Data cd : curr.value) {
4034                                 if("pending".equals(cd.status)) {
4035                                                 // Check for right record.  Need ID, or (Ticket&Trans.User==Appr)
4036                                         // If Default ID
4037                                         boolean delegatedAction = ques.isDelegated(trans, user, cd.approver, delegateCache);
4038                                         String delegator = cd.approver;
4039                                         if(updt.id!=null || 
4040                                                 (updt.ticket!=null && user.equals(cd.approver)) ||
4041                                                 (updt.ticket!=null && delegatedAction)) {
4042                                                 if(updt.ticket.equals(cd.ticket)) {
4043                                                         Changed ch = new Changed();
4044                                                         cd.id = ch.changed(cd.id,updt.id);
4045 //                                                      cd.ticket = changed(cd.ticket,updt.ticket);
4046                                                         cd.user = ch.changed(cd.user,updt.user);
4047                                                         cd.approver = ch.changed(cd.approver,updt.approver);
4048                                                         cd.type = ch.changed(cd.type,updt.type);
4049                                                         cd.status = ch.changed(cd.status,updt.status);
4050                                                         cd.memo = ch.changed(cd.memo,updt.memo);
4051                                                         cd.operation = ch.changed(cd.operation,updt.operation);
4052                                                         cd.updated = ch.changed(cd.updated,updt.updated==null?new Date():updt.updated);
4053                                                         if(updt.status.equals("denied")) {
4054                                                                 cd.last_notified = null;
4055                                                         }
4056                                                         if(cd.ticket!=null) {
4057                                                                 FutureDAO.Data fdd = futureCache.get(cd.ticket);
4058                                                                 if(fdd==null) { // haven't processed ticket yet
4059                                                                         Result<FutureDAO.Data> rfdd = ques.futureDAO.readPrimKey(trans, cd.ticket);
4060                                                                         if(rfdd.isOK()) {
4061                                                                                 fdd = rfdd.value; // null is ok
4062                                                                         } else {
4063                                                                                 fdd = hasDeleted;
4064                                                                         }
4065                                                                         futureCache.put(cd.ticket, fdd); // processed this Ticket... don't do others on this ticket
4066                                                                 }
4067                                                                 if(fdd==hasDeleted) { // YES, by Object
4068                                                                         cd.ticket = null;
4069                                                                         cd.status = "ticketDeleted";
4070                                                                         ch.hasChanged(true);
4071                                                                 } else {
4072                                                                         FUTURE_OP fop = FUTURE_OP.toFO(cd.operation);
4073                                                                         if(fop==null) {
4074                                                                                 trans.info().printf("Approval Status %s is not actionable",cd.status);
4075                                                                         } else if(apprByTicket!=null) {
4076                                                                                 Result<OP_STATUS> rv = func.performFutureOp(trans, fop, fdd, apprByTicket,func.urDBLookup);
4077                                                                                 if (rv.isOK()) {
4078                                                                                         switch(rv.value) {
4079                                                                                                 case E:
4080                                                                                                         if (delegatedAction) {
4081                                                                                                                 trans.audit().printf(APPR_FMT,user,updt.status,cd.memo,cd.user,delegator);
4082                                                                                                         }
4083                                                                                                         futureCache.put(cd.ticket, hasDeleted);
4084                                                                                                         break;
4085                                                                                                 case D:
4086                                                                                                 case L:
4087                                                                                                         ch.hasChanged(true);
4088                                                                                                         trans.audit().printf(APPR_FMT,user,rv.value.desc(),cd.memo,cd.user,delegator);
4089                                                                                                         futureCache.put(cd.ticket, hasDeleted);
4090                                                                                                         break;
4091                                                                                                 default:
4092                                                                                         }
4093                                                                                 } else {
4094                                                                                         trans.info().log(rv.toString());
4095                                                                                 }
4096                                                                         }
4097
4098                                                                 }
4099                                                                 ++numProcessed;
4100                                                         }
4101                                                         if(ch.hasChanged()) {
4102                                                                 ques.approvalDAO.update(trans, cd, true);
4103                                                         }
4104                                                 }
4105                                         }
4106                                     }
4107                             }
4108                         }
4109                 }
4110
4111                 if(numApprs==numProcessed) {
4112                         return Result.ok();
4113                 }
4114                 return Result.err(Status.ERR_ActionNotCompleted,numProcessed + " out of " + numApprs + " completed");
4115
4116         }
4117         
4118         private static class Changed {
4119                 private boolean hasChanged = false;
4120
4121                 public<T> T changed(T src, T proposed) {
4122                         if(proposed==null || (src!=null && src.equals(proposed))) {
4123                             return src;
4124                         }
4125                         hasChanged=true;
4126                         return proposed;
4127                 }
4128
4129                 public void hasChanged(boolean b) {
4130                         hasChanged=b;
4131                 }
4132
4133                 public boolean hasChanged() {
4134                         return hasChanged;
4135                 }
4136         }
4137
4138         @Override
4139         public Result<APPROVALS> getApprovalsByUser(AuthzTrans trans, String user) {
4140                 final Validator v = new ServiceValidator();
4141                 if(v.nullOrBlank("User", user).err()) { 
4142                         return Result.err(Status.ERR_BadData,v.errs());
4143                 }
4144
4145                 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByUser(trans, user);
4146                 if(rapd.isOK()) {
4147                         return mapper.approvals(rapd.value);
4148                 } else {
4149                         return Result.err(rapd);
4150                 }
4151 }
4152
4153         @Override
4154         public Result<APPROVALS> getApprovalsByTicket(AuthzTrans trans, String ticket) {
4155                 final Validator v = new ServiceValidator();
4156                 if(v.nullOrBlank("Ticket", ticket).err()) { 
4157                         return Result.err(Status.ERR_BadData,v.errs());
4158                 }
4159                 UUID uuid;
4160                 try {
4161                         uuid = UUID.fromString(ticket);
4162                 } catch (IllegalArgumentException e) {
4163                         return Result.err(Status.ERR_BadData,e.getMessage());
4164                 }
4165         
4166                 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByTicket(trans, uuid);
4167                 if(rapd.isOK()) {
4168                         return mapper.approvals(rapd.value);
4169                 } else {
4170                         return Result.err(rapd);
4171                 }
4172         }
4173         
4174         @Override
4175         public Result<APPROVALS> getApprovalsByApprover(AuthzTrans trans, String approver) {
4176                 final Validator v = new ServiceValidator();
4177                 if(v.nullOrBlank("Approver", approver).err()) {
4178                         return Result.err(Status.ERR_BadData,v.errs());
4179                 }
4180                 
4181                 List<ApprovalDAO.Data> listRapds = new ArrayList<ApprovalDAO.Data>();
4182                 
4183                 Result<List<ApprovalDAO.Data>> myRapd = ques.approvalDAO.readByApprover(trans, approver);
4184                 if(myRapd.notOK()) {
4185                         return Result.err(myRapd);
4186                 }
4187                 
4188                 listRapds.addAll(myRapd.value);
4189                 
4190                 Result<List<DelegateDAO.Data>> delegatedFor = ques.delegateDAO.readByDelegate(trans, approver);
4191                 if (delegatedFor.isOK()) {
4192                         for (DelegateDAO.Data dd : delegatedFor.value) {
4193                                 if (dd.expires.after(new Date())) {
4194                                         String delegator = dd.user;
4195                                         Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO.readByApprover(trans, delegator);
4196                                         if (rapd.isOK()) {
4197                                                 for (ApprovalDAO.Data d : rapd.value) { 
4198                                                         if (!d.user.equals(trans.user())) {
4199                                                                 listRapds.add(d);
4200                                                         }
4201                                                 }
4202                                         }
4203                                 }
4204                         }
4205                 }
4206                 
4207                 return mapper.approvals(listRapds);
4208         }
4209         
4210         /* (non-Javadoc)
4211          * @see org.onap.aaf.auth.service.AuthzService#clearCache(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
4212          */
4213         @Override
4214         public Result<Void> cacheClear(AuthzTrans trans, String cname) {
4215                 if(ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4216                         return ques.clearCache(trans,cname);
4217                 }
4218                 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4219                                 trans.user(),ROOT_NS,CACHE,cname);
4220         }
4221
4222         /* (non-Javadoc)
4223          * @see org.onap.aaf.auth.service.AuthzService#cacheClear(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.Integer)
4224          */
4225         @Override
4226         public Result<Void> cacheClear(AuthzTrans trans, String cname, int[] segment) {
4227                 if(ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4228                         Result<Void> v=null;
4229                         for(int i: segment) {
4230                                 v=ques.cacheClear(trans,cname,i);
4231                         }
4232                         if(v!=null) {
4233                                 return v;
4234                         }
4235                 }
4236                 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4237                                 trans.user(),ROOT_NS,CACHE,cname);
4238         }
4239
4240         /* (non-Javadoc)
4241          * @see org.onap.aaf.auth.service.AuthzService#dbReset(org.onap.aaf.auth.env.test.AuthzTrans)
4242          */
4243         @Override
4244         public void dbReset(AuthzTrans trans) {
4245                 ques.historyDAO.reportPerhapsReset(trans, null);
4246         }
4247
4248 }
4249