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