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