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