Post Init Service Starter
[aaf/authz.git] / auth / auth-service / src / main / java / org / onap / aaf / auth / service / mapper / Mapper_2_0.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.service.mapper;
23
24 import java.nio.ByteBuffer;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.Date;
30 import java.util.GregorianCalendar;
31 import java.util.List;
32 import java.util.UUID;
33
34 import javax.xml.datatype.XMLGregorianCalendar;
35
36 import org.onap.aaf.auth.dao.Bytification;
37 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
38 import org.onap.aaf.auth.dao.cass.CertDAO;
39 import org.onap.aaf.auth.dao.cass.CredDAO;
40 import org.onap.aaf.auth.dao.cass.DelegateDAO;
41 import org.onap.aaf.auth.dao.cass.FutureDAO;
42 import org.onap.aaf.auth.dao.cass.HistoryDAO;
43 import org.onap.aaf.auth.dao.cass.Namespace;
44 import org.onap.aaf.auth.dao.cass.NsSplit;
45 import org.onap.aaf.auth.dao.cass.NsType;
46 import org.onap.aaf.auth.dao.cass.PermDAO;
47 import org.onap.aaf.auth.dao.cass.RoleDAO;
48 import org.onap.aaf.auth.dao.cass.Status;
49 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
50 import org.onap.aaf.auth.dao.cass.DelegateDAO.Data;
51 import org.onap.aaf.auth.dao.hl.Question;
52 import org.onap.aaf.auth.dao.hl.Question.Access;
53 import org.onap.aaf.auth.env.AuthzTrans;
54 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
55 import org.onap.aaf.auth.layer.Result;
56 import org.onap.aaf.auth.org.Organization;
57 import org.onap.aaf.auth.org.Organization.Expiration;
58 import org.onap.aaf.auth.rserv.Pair;
59 import org.onap.aaf.auth.service.MayChange;
60 import org.onap.aaf.cadi.aaf.marshal.CertsMarshal;
61 import org.onap.aaf.cadi.util.Vars;
62 import org.onap.aaf.misc.env.Env;
63 import org.onap.aaf.misc.env.TimeTaken;
64 import org.onap.aaf.misc.env.util.Chrono;
65 import org.onap.aaf.misc.rosetta.Marshal;
66
67 import aaf.v2_0.Api;
68 import aaf.v2_0.Approval;
69 import aaf.v2_0.Approvals;
70 import aaf.v2_0.Certs;
71 import aaf.v2_0.Certs.Cert;
72 import aaf.v2_0.CredRequest;
73 import aaf.v2_0.Delg;
74 import aaf.v2_0.DelgRequest;
75 import aaf.v2_0.Delgs;
76 import aaf.v2_0.Error;
77 import aaf.v2_0.History;
78 import aaf.v2_0.History.Item;
79 import aaf.v2_0.Keys;
80 import aaf.v2_0.NsRequest;
81 import aaf.v2_0.Nss;
82 import aaf.v2_0.Nss.Ns;
83 import aaf.v2_0.Perm;
84 import aaf.v2_0.PermKey;
85 import aaf.v2_0.PermRequest;
86 import aaf.v2_0.Perms;
87 import aaf.v2_0.Pkey;
88 import aaf.v2_0.Request;
89 import aaf.v2_0.Role;
90 import aaf.v2_0.RolePermRequest;
91 import aaf.v2_0.RoleRequest;
92 import aaf.v2_0.Roles;
93 import aaf.v2_0.UserRole;
94 import aaf.v2_0.UserRoleRequest;
95 import aaf.v2_0.UserRoles;
96 import aaf.v2_0.Users;
97 import aaf.v2_0.Users.User;
98
99 public class Mapper_2_0 implements Mapper<Nss, Perms, Pkey, Roles, Users, UserRoles, Delgs, Certs, Keys, Request, History, Error, Approvals> {
100     private Question q;
101
102     public Mapper_2_0(Question q) {
103         this.q = q;
104     }
105     
106     /* (non-Javadoc)
107      * @see org.onap.aaf.auth.service.mapper.Mapper#ns(java.lang.Object, org.onap.aaf.auth.service.mapper.Mapper.Holder)
108      */
109     @Override
110     public Result<Namespace> ns(AuthzTrans trans, Request base) {
111         NsRequest from = (NsRequest)base;
112         Namespace namespace = new Namespace();
113         namespace.name = from.getName();
114         namespace.admin = from.getAdmin();
115         namespace.owner = from.getResponsible();
116         namespace.description = from.getDescription();
117         trans.checkpoint(namespace.name, Env.ALWAYS);
118         
119         NsType nt = NsType.fromString(from.getType());
120         if (nt.equals(NsType.UNKNOWN)) {
121             String ns = namespace.name;
122             int count = 0;
123             for (int i=ns.indexOf('.');
124                     i>=0;
125                     i=ns.indexOf('.',i+1)) {
126                 ++count;
127             }
128             switch(count) {
129                 case 0: nt = NsType.ROOT;break;
130                 case 1: nt = NsType.COMPANY;break;
131                 default: nt = NsType.APP;
132             }
133         }
134         namespace.type = nt.type;
135         
136         return Result.ok(namespace);
137     }
138
139     @Override
140     public Result<Nss> nss(AuthzTrans trans, Namespace from, Nss to) {
141         List<Ns> nss = to.getNs();
142         Ns ns = new Ns();
143         ns.setName(from.name);
144         if (from.admin!=null)ns.getAdmin().addAll(from.admin);
145         if (from.owner!=null)ns.getResponsible().addAll(from.owner);
146         if (from.attrib!=null) {
147             for (Pair<String,String> attrib : from.attrib) {
148                 Ns.Attrib toAttrib = new Ns.Attrib();
149                 toAttrib.setKey(attrib.x);
150                 toAttrib.setValue(attrib.y);
151                 ns.getAttrib().add(toAttrib);
152             }
153         }
154
155         ns.setDescription(from.description);
156         nss.add(ns);
157         return Result.ok(to);
158     }
159
160     /**
161      * Note: Prevalidate if NS given is allowed to be seen before calling
162      */
163     @Override
164     public Result<Nss> nss(AuthzTrans trans, Collection<Namespace> from, Nss to) {
165         List<Ns> nss = to.getNs();
166         for (Namespace nd : from) {
167             Ns ns = new Ns();
168             ns.setName(nd.name);
169             if (nd.admin!=null) {
170                 ns.getAdmin().addAll(nd.admin);
171             }
172             if (nd.owner!=null) {
173                 ns.getResponsible().addAll(nd.owner);
174             }
175             ns.setDescription(nd.description);
176             if (nd.attrib!=null) {
177                 for (Pair<String,String> attrib : nd.attrib) {
178                     Ns.Attrib toAttrib = new Ns.Attrib();
179                     toAttrib.setKey(attrib.x);
180                     toAttrib.setValue(attrib.y);
181                     ns.getAttrib().add(toAttrib);
182                 }
183             }
184
185             nss.add(ns);
186         }
187         return Result.ok(to);
188     }
189
190     @Override
191     public Result<Perms> perms(AuthzTrans trans, List<PermDAO.Data> from, Perms to, boolean filter) {
192         List<Perm> perms = to.getPerm();
193         final boolean addNS = trans.requested(REQD_TYPE.ns);
194         TimeTaken tt = trans.start("Filter Perms before return", Env.SUB);
195         try {
196             if (from!=null) {
197                 for (PermDAO.Data data : from) {
198                     if (!filter || q.mayUser(trans, trans.user(), data, Access.read).isOK()) {
199                         Perm perm = new Perm();
200                         perm.setType(data.fullType());
201                         perm.setInstance(data.instance);
202                         perm.setAction(data.action);
203                         perm.setDescription(data.description);
204                         if (addNS) {
205                             perm.setNs(data.ns);
206                         }
207                         for (String role : data.roles(false)) {
208                             perm.getRoles().add(role);
209                         }
210                         perms.add(perm);
211                     }
212                 }
213             }
214         } finally {
215             tt.done();
216         }
217          
218         tt = trans.start("Sort Perms", Env.SUB);
219         try {
220             Collections.sort(perms, new Comparator<Perm>() {
221                 @Override
222                 public int compare(Perm perm1, Perm perm2) {
223                     int typeCompare = perm1.getType().compareToIgnoreCase(perm2.getType());
224                     if (typeCompare == 0) {
225                         int instanceCompare = perm1.getInstance().compareToIgnoreCase(perm2.getInstance());
226                         if (instanceCompare == 0) {
227                             return perm1.getAction().compareToIgnoreCase(perm2.getAction());
228                         }
229                         return instanceCompare;
230                     }
231                     return typeCompare;
232                 }    
233             });
234         } finally {
235             tt.done();
236         }
237         return Result.ok(to);
238     }
239     
240     @Override
241     public Result<Perms> perms(AuthzTrans trans, List<PermDAO.Data> from, Perms to, String[] nss, boolean filter) {
242         List<Perm> perms = to.getPerm();
243         TimeTaken tt = trans.start("Filter Perms before return", Env.SUB);
244         try {
245             if (from!=null) {
246                 boolean inNSS;
247                 for (PermDAO.Data data : from) {
248                     inNSS=false;
249                     for (int i=0;!inNSS && i<nss.length;++i) {
250                         if (nss[i].equals(data.ns)) {
251                             inNSS=true;
252                         }
253                     }
254                     if (inNSS && (!filter || q.mayUser(trans, trans.user(), data, Access.read).isOK())) {
255                         Perm perm = new Perm();
256                         perm.setType(data.fullType());
257                         perm.setInstance(data.instance);
258                         perm.setAction(data.action);
259                         for (String role : data.roles(false)) {
260                             perm.getRoles().add(role);
261                         }
262                         perm.setDescription(data.description);
263                         perms.add(perm);
264                     }
265                 }
266             }
267         } finally {
268             tt.done();
269         }
270          
271         tt = trans.start("Sort Perms", Env.SUB);
272         try {
273             Collections.sort(perms, new Comparator<Perm>() {
274                 @Override
275                 public int compare(Perm perm1, Perm perm2) {
276                     int typeCompare = perm1.getType().compareToIgnoreCase(perm2.getType());
277                     if (typeCompare == 0) {
278                         int instanceCompare = perm1.getInstance().compareToIgnoreCase(perm2.getInstance());
279                         if (instanceCompare == 0) {
280                             return perm1.getAction().compareToIgnoreCase(perm2.getAction());
281                         }
282                         return instanceCompare;
283                     }
284                     return typeCompare;
285                 }    
286             });
287         } finally {
288             tt.done();
289         }
290         return Result.ok(to);
291     }
292
293     @Override
294     public Result<List<PermDAO.Data>> perms(AuthzTrans trans, Perms perms) {
295         List<PermDAO.Data> lpd = new ArrayList<>();
296         for (Perm p : perms.getPerm()) {
297             Result<NsSplit> nss = q.deriveNsSplit(trans, p.getType());
298             PermDAO.Data pd = new PermDAO.Data();
299             if (nss.isOK()) { 
300                 pd.ns=nss.value.ns;
301                 pd.type = nss.value.name;
302                 pd.instance = p.getInstance();
303                 pd.action = p.getAction();
304                 for (String role : p.getRoles()) {
305                     pd.roles(true).add(role);
306                 }
307                 lpd.add(pd);
308             } else {
309                 return Result.err(nss);
310             }
311         }
312         return Result.ok(lpd);
313     }
314
315     
316     @Override
317     public Result<PermDAO.Data> permkey(AuthzTrans trans, Pkey from) {
318         return q.permFrom(trans, from.getType(),from.getInstance(),from.getAction());
319     }
320     
321     @Override
322     public Result<PermDAO.Data> permFromRPRequest(AuthzTrans trans, Request req) {
323         RolePermRequest from = (RolePermRequest)req;
324         Pkey perm = from.getPerm();
325         if (perm==null)return Result.err(Status.ERR_NotFound, "Permission not found");
326         Result<NsSplit> nss = q.deriveNsSplit(trans, perm.getType());
327         PermDAO.Data pd = new PermDAO.Data();
328         if (nss.isOK()) { 
329             pd.ns=nss.value.ns;
330             pd.type = nss.value.name;
331             pd.instance = from.getPerm().getInstance();
332             pd.action = from.getPerm().getAction();
333             trans.checkpoint(pd.fullPerm(), Env.ALWAYS);
334             
335             String[] roles = {};
336             
337             if (from.getRole() != null) {
338                 roles = from.getRole().split(",");
339             }
340             for (String role : roles) { 
341                 pd.roles(true).add(role);
342             }
343             return Result.ok(pd);
344         } else {
345             return Result.err(nss);
346         }
347     }
348     
349     @Override
350     public Result<RoleDAO.Data> roleFromRPRequest(AuthzTrans trans, Request req) {
351         RolePermRequest from = (RolePermRequest)req;
352         Result<NsSplit> nss = q.deriveNsSplit(trans, from.getRole());
353         RoleDAO.Data rd = new RoleDAO.Data();
354         if (nss.isOK()) { 
355             rd.ns = nss.value.ns;
356             rd.name = nss.value.name;
357             trans.checkpoint(rd.fullName(), Env.ALWAYS);
358             return Result.ok(rd);
359         } else {
360             return Result.err(nss);
361         }
362     }
363     
364     @Override
365     public Result<PermDAO.Data> perm(AuthzTrans trans, Request req) {
366         PermRequest from = (PermRequest)req;
367         Result<NsSplit> nss = q.deriveNsSplit(trans, from.getType());
368         PermDAO.Data pd = new PermDAO.Data();
369         if (nss.isOK()) { 
370             pd.ns=nss.value.ns;
371             pd.type = nss.value.name;
372             pd.instance = from.getInstance();
373             pd.action = from.getAction();
374             pd.description = from.getDescription();
375             trans.checkpoint(pd.fullPerm(), Env.ALWAYS);
376             return Result.ok(pd);
377         } else {
378             return Result.err(nss);
379         }
380     }
381     
382     @Override
383     public Request ungrantRequest(AuthzTrans trans, String role, String type, String instance, String action) {
384         RolePermRequest rpr = new RolePermRequest();
385         Pkey pkey = new Pkey();
386         pkey.setType(type);
387         pkey.setInstance(instance);
388         pkey.setAction(action);
389         rpr.setPerm(pkey);
390         
391         rpr.setRole(role);
392         return rpr;
393     }
394
395     @Override
396     public Result<RoleDAO.Data> role(AuthzTrans trans, Request base) {
397         RoleRequest from = (RoleRequest)base;
398         Result<NsSplit> nss = q.deriveNsSplit(trans, from.getName());
399         if (nss.isOK()) {
400             RoleDAO.Data to = new RoleDAO.Data();
401             to.ns = nss.value.ns;
402             to.name = nss.value.name;
403             to.description = from.getDescription();
404             trans.checkpoint(to.fullName(), Env.ALWAYS);
405
406             return Result.ok(to);
407         } else {
408             return Result.err(nss);
409         }
410     }
411
412     /* (non-Javadoc)
413      * @see org.onap.aaf.auth.service.mapper.Mapper#roles(java.util.List)
414      */
415     @Override
416     public Result<Roles> roles(AuthzTrans trans, List<RoleDAO.Data> from, Roles to, boolean filter) {
417         final boolean needNS = trans.requested(REQD_TYPE.ns); 
418         for (RoleDAO.Data frole : from) {
419             // Only Add Data to view if User is allowed to see this Role
420             if (!filter || q.mayUser(trans, trans.user(), frole,Access.read).isOK()) {
421                 Role role = new Role();
422                 role.setName(frole.ns + '.' + frole.name);
423                 role.setDescription(frole.description);
424                 if (needNS) {
425                     role.setNs(frole.ns);
426                 }
427                 for (String p : frole.perms(false)) { // can see any Perms in the Role he has permission for
428                     Result<String[]> rpa = PermDAO.Data.decodeToArray(trans,q,p);
429                     if (rpa.notOK())
430                         return Result.err(rpa);
431                     
432                     String[] pa = rpa.value;
433                     Pkey pKey = new Pkey();
434                     pKey.setType(pa[0]+'.'+pa[1]);
435                     pKey.setInstance(pa[2]);
436                     pKey.setAction(pa[3]);
437                     role.getPerms().add(pKey);
438                 }
439                 to.getRole().add(role);
440             }
441         }
442         return Result.ok(to);
443     }
444
445     /*
446      * (non-Javadoc)
447      * @see org.onap.aaf.auth.service.mapper.Mapper#users(java.util.Collection, java.lang.Object)
448      * 
449      * Note: Prevalidate all data for permission to view
450      */
451     @Override
452     public Result<Users> users(AuthzTrans trans, Collection<UserRoleDAO.Data> from, Users to) {
453         List<User> cu = to.getUser();
454         for (UserRoleDAO.Data urd : from) {
455             User user = new User();
456             user.setId(urd.user);
457             if (urd.expires!=null) {
458                 user.setExpires(Chrono.timeStamp(urd.expires));
459             }
460             cu.add(user);
461         }
462         return Result.ok(to);
463     }
464
465     /*
466      * (non-Javadoc)
467      * @see org.onap.aaf.auth.service.mapper.Mapper#users(java.util.Collection, java.lang.Object)
468      * 
469      * Note: Prevalidate all data for permission to view
470      */
471     @Override
472     public Result<UserRoles> userRoles(AuthzTrans trans, Collection<UserRoleDAO.Data> from, UserRoles to) {
473         List<UserRole> cu = to.getUserRole();
474         for (UserRoleDAO.Data urd : from) {
475             UserRole ur = new UserRole();
476             ur.setUser(urd.user);
477             ur.setRole(urd.role);
478             ur.setExpires(Chrono.timeStamp(urd.expires));
479             cu.add(ur);
480         }
481         return Result.ok(to);
482     }
483
484     @Override
485     public Result<UserRoleDAO.Data> userRole(AuthzTrans trans, Request base) {
486         try {
487             UserRoleRequest from = (UserRoleRequest)base;
488
489             // Setup UserRoleData, either for immediate placement, or for futureIt i
490             UserRoleDAO.Data to = new UserRoleDAO.Data();
491             if (from.getUser() != null) {
492                 to.user = from.getUser();
493             }
494             if (from.getRole() != null) {
495                 to.role(trans,q,from.getRole());
496             }
497             to.expires = getExpires(trans.org(),Expiration.UserInRole,base,from.getUser());
498             trans.checkpoint(to.toString(), Env.ALWAYS);
499
500             return Result.ok(to);
501         } catch (Exception t) {
502             return Result.err(Status.ERR_BadData,t.getMessage());
503         }
504     }
505
506     @Override
507     public Result<CredDAO.Data> cred(AuthzTrans trans, Request base, boolean requiresPass) {
508         CredRequest from = (CredRequest)base;
509         CredDAO.Data to = new CredDAO.Data();
510         to.id=from.getId();
511         to.ns = Question.domain2ns(to.id);
512         String passwd = from.getPassword();
513         if (requiresPass) {
514             String ok = trans.org().isValidPassword(trans, to.id,passwd);
515             if (ok.length()>0) {
516                 return Result.err(Status.ERR_BadData,ok);
517             }
518         } else {
519             to.type=0;
520         }
521         if (passwd != null) {
522             to.cred = ByteBuffer.wrap(passwd.getBytes());
523             to.type = CredDAO.RAW; 
524         } else {
525             to.type = 0;
526         }
527         
528         // Note: Ensure requested EndDate created will match Organization Password Rules
529         //  P.S. Do not apply TempPassword rule here. Do that when you know you are doing a Create/Reset (see Service)
530         to.expires = getExpires(trans.org(),Expiration.Password,base,from.getId());
531         trans.checkpoint(to.id, Env.ALWAYS);
532
533         return Result.ok(to);
534     }
535     
536     @Override
537     public Result<Users> cred(List<CredDAO.Data> from, Users to) {
538         List<User> cu = to.getUser();
539         for (CredDAO.Data cred : from) {
540             User user = new User();
541             user.setId(cred.id);
542             user.setExpires(Chrono.timeStamp(cred.expires));
543             user.setType(cred.type);
544             user.setTag(cred.tag);
545             cu.add(user);
546         }
547         return Result.ok(to);
548     }
549     
550     @Override
551     public Result<Certs> cert(List<CertDAO.Data> from, Certs to) {
552         List<Cert> lc = to.getCert();
553         for (CertDAO.Data fcred : from) {
554             Cert cert = new Cert();
555             cert.setId(fcred.id);
556             cert.setX500(fcred.x500);
557             /**TODO - change Interface 
558              * @deprecated */
559             cert.setFingerprint(fcred.serial.toByteArray());
560             lc.add(cert);
561         }
562         return Result.ok(to);
563     }
564
565     /**
566      * Analyze whether Requests should be acted on now, or in the future, based on Start Date, and whether the requester
567      * is allowed to change this value directly
568      * 
569      * Returning Result.OK means it should be done in the future.
570      * Returning Result.ACC_Now means to act on table change now.
571      */
572     @Override
573     public Result<FutureDAO.Data> future(AuthzTrans trans, String table, Request from, 
574                 Bytification content, boolean enableApproval,  Memo memo, MayChange mc) {
575         Result<?> rMayChange;
576         boolean needsAppr = enableApproval?trans.requested(REQD_TYPE.future):false; 
577         if (!needsAppr && (needsAppr = (rMayChange=mc.mayChange()).notOK())) {
578             if (enableApproval) {
579                 if (!trans.requested(AuthzTrans.REQD_TYPE.future)) {
580                     return Result.err(rMayChange);
581                 }
582             } else {
583                 return Result.err(rMayChange);
584             }
585         }
586         GregorianCalendar now = new GregorianCalendar(); 
587         GregorianCalendar start = from.getStart()==null?now:from.getStart().toGregorianCalendar();
588         
589         GregorianCalendar expires = trans.org().expiration(start, Expiration.Future);
590         XMLGregorianCalendar xgc;
591         if ((xgc=from.getEnd())!=null) {
592             GregorianCalendar fgc = xgc.toGregorianCalendar();
593             expires = expires.before(fgc)?expires:fgc; // Min of desired expiration, and Org expiration
594         }
595         
596         //TODO needs two answers from this.  What's the NSS, and may Change.
597         FutureDAO.Data fto;
598         if (start.after(now) || needsAppr ) {
599             //String user = trans.user();
600             fto = new FutureDAO.Data();
601             fto.target=table;
602             fto.memo = memo.get();
603             fto.start = start.getTime();
604             fto.expires = expires.getTime();
605             if (needsAppr) { // Need to add Approvers...
606                 /*
607                 Result<Data> rslt = mc.getNsd();
608                 if (rslt.notOKorIsEmpty())return Result.err(rslt);
609                 appr.addAll(mc.getNsd().value.responsible);
610                 try {
611                     //Note from 2013 Is this getting Approvers for user only?  What about Delegates?
612                     // 3/25/2014.  Approvers are set by Corporate policy.  We don't have to worry here about what that means.
613                     // It is important to get Delegates, if necessary, at notification time
614                     // If we add delegates now, it will get all confused as to who is actually responsible.
615                     for (Organization.User ou : org.getApprovers(trans, user)) {
616                         appr.add(ou.email);
617                     }
618                 } catch (Exception e) {
619                     return Result.err(Status.ERR_Policy,org.getName() + " did not respond with Approvers: " + e.getLocalizedMessage());
620                 }
621                 */
622             }
623             try {
624                 fto.construct = content.bytify();
625             } catch (Exception e) {
626                 return Result.err(Status.ERR_BadData,"Data cannot be saved for Future.");
627             }
628         } else {
629             return Result.err(Status.ACC_Now, "Make Data changes now.");
630         }
631         return Result.ok(fto);
632     }
633
634
635     /* (non-Javadoc)
636      * @see org.onap.aaf.auth.service.mapper.Mapper#history(java.util.List)
637      */
638     @Override
639     public Result<History> history(AuthzTrans trans, List<HistoryDAO.Data> history, final int sort) {
640         History hist = new History();
641         List<Item> items = hist.getItem();
642         for (HistoryDAO.Data data : history) {
643             History.Item item = new History.Item();
644             item.setYYYYMM(Integer.toString(data.yr_mon));
645             Date date = Chrono.uuidToDate(data.id);
646             item.setTimestamp(Chrono.timeStamp(date));
647             item.setAction(data.action);
648             item.setMemo(data.memo);
649             item.setSubject(data.subject);
650             item.setTarget(data.target);
651             item.setUser(data.user);
652             items.add(item);
653         }
654         
655         if (sort != 0) {
656             TimeTaken tt = trans.start("Sort ", Env.SUB);
657             try {
658                 java.util.Collections.sort(items, new Comparator<Item>() {
659                     @Override
660                     public int compare(Item o1, Item o2) {
661                         return sort*(o1.getTimestamp().compare(o2.getTimestamp()));
662                     }
663                 });
664             } finally {
665                 tt.done();
666             }
667         }
668         return Result.ok(hist);
669     }
670
671     @Override
672     public Error errorFromMessage(StringBuilder holder, String msgID, String text, String... var) {
673         Error err = new Error();
674         err.setMessageId(msgID);
675         // AT&T Restful Error Format requires numbers "%" placements
676         err.setText(Vars.convert(holder, text, (Object[])var));
677         for (String s : var) {
678             err.getVariables().add(s);
679         }
680         return err;
681     }
682     
683     @Override
684     public Class<?> getClass(API api) {
685         switch(api) {
686             case NSS:  return Nss.class;
687             case NS_REQ: return NsRequest.class;
688             case PERMS: return Perms.class;
689             case PERM_KEY: return PermKey.class;
690             case ROLES: return Roles.class;
691             case ROLE: return Role.class;
692             case USERS: return Users.class;
693             case DELGS: return Delgs.class;
694             case CERTS: return Certs.class;
695             case DELG_REQ: return DelgRequest.class;
696             case PERM_REQ: return PermRequest.class;
697             case ROLE_REQ:  return RoleRequest.class;
698             case CRED_REQ:  return CredRequest.class;
699             case USER_ROLE_REQ:  return UserRoleRequest.class;
700             case USER_ROLES: return UserRoles.class;
701             case ROLE_PERM_REQ:  return RolePermRequest.class;
702             case APPROVALS: return Approvals.class;
703             case KEYS: return Keys.class;
704             case HISTORY: return History.class;
705 //            case MODEL: return Model.class;
706             case ERROR: return Error.class;
707             case API: return Api.class;
708             case VOID: return Void.class;
709         }
710         return null;
711     }
712
713     @SuppressWarnings("unchecked")
714     @Override
715     public <A> A newInstance(API api) {
716         switch(api) {
717             case NS_REQ: return (A) new NsRequest();
718             case NSS: return (A) new Nss();
719             case PERMS: return (A)new Perms();
720             case PERM_KEY: return (A)new PermKey();
721             case ROLES: return (A)new Roles();
722             case ROLE: return (A)new Role();
723             case USERS: return (A)new Users();
724             case DELGS: return (A)new Delgs();
725             case CERTS: return (A)new Certs();
726             case PERM_REQ: return (A)new PermRequest();
727             case CRED_REQ: return (A)new CredRequest();
728             case ROLE_REQ:  return (A)new RoleRequest();
729             case USER_ROLE_REQ:  return (A)new UserRoleRequest();
730             case USER_ROLES:  return (A)new UserRoles();
731             case ROLE_PERM_REQ:  return (A)new RolePermRequest();
732             case HISTORY: return (A)new History();
733             case KEYS: return (A)new Keys();
734             //case MODEL: return (A)new Model();
735             case ERROR: return (A)new Error();
736             case API: return (A)new Api();
737             case VOID: return null;
738             
739             case APPROVALS:    return (A) new Approvals();
740             case DELG_REQ: return (A) new DelgRequest();
741         }
742         return null;
743     }
744     
745     @SuppressWarnings("unchecked")
746     /**
747      * Get Typed Marshaler as they are defined
748      * 
749      * @param api
750      * @return
751      */
752     public <A> Marshal<A> getMarshal(API api) {
753         switch(api) {
754             case CERTS: return (Marshal<A>) new CertsMarshal();
755             default:
756                 return null;
757         }
758     }
759
760     @Override
761     public Result<Approvals> approvals(List<ApprovalDAO.Data> lAppr) {
762         Approvals apprs = new Approvals();
763         List<Approval> lappr = apprs.getApprovals();
764         Approval a;
765         for (ApprovalDAO.Data appr : lAppr) {
766             a = new Approval();
767             a.setId(appr.id.toString());
768             if (appr.ticket==null) {
769                 a.setTicket(null);
770             } else {
771                 a.setTicket(appr.ticket.toString());
772             }
773             a.setUser(appr.user);
774             a.setApprover(appr.approver);
775             a.setType(appr.type);
776             a.setStatus(appr.status);
777             a.setMemo(appr.memo);
778             a.setOperation(appr.operation);
779             a.setUpdated(Chrono.timeStamp(appr.updated));
780             lappr.add(a);
781         }
782         return Result.ok(apprs);
783     }
784     
785     @Override
786     public Result<List<ApprovalDAO.Data>> approvals(Approvals apprs) {
787         List<ApprovalDAO.Data>  lappr = new ArrayList<>();
788         for (Approval a : apprs.getApprovals()) {
789             ApprovalDAO.Data ad = new ApprovalDAO.Data();
790             String str = a.getId();
791             if (str!=null)ad.id=UUID.fromString(str);
792             str = a.getTicket();
793             if (str!=null)ad.ticket=UUID.fromString(str);
794             ad.user=a.getUser();
795             ad.approver=a.getApprover();
796             ad.type=a.getType();
797             ad.status=a.getStatus();
798             ad.operation=a.getOperation();
799             ad.memo=a.getMemo();
800             
801             XMLGregorianCalendar xgc = a.getUpdated();
802             if (xgc!=null)ad.updated=xgc.toGregorianCalendar().getTime();
803             lappr.add(ad);
804         }
805         return Result.ok(lappr);
806     }
807
808     @Override
809     public Result<Delgs> delegate(List<DelegateDAO.Data> lDelg) {
810         Delgs delgs = new Delgs();
811         List<Delg> ldelg = delgs.getDelgs();
812         Delg d;
813         for (DelegateDAO.Data del: lDelg) {
814             d = new Delg();
815             d.setUser(del.user);
816             d.setDelegate(del.delegate);
817             if (del.expires!=null)d.setExpires(Chrono.timeStamp(del.expires));
818             ldelg.add(d);
819         }
820         return Result.ok(delgs);
821     }
822
823     @Override
824     public Result<Data> delegate(AuthzTrans trans, Request base) {
825         try {
826             DelgRequest from = (DelgRequest)base;
827             DelegateDAO.Data to = new DelegateDAO.Data();
828             String user = from.getUser();
829             to.user = user;
830             String delegate = from.getDelegate();
831             to.delegate = delegate;
832             to.expires = getExpires(trans.org(),Expiration.UserDelegate,base,from.getUser());
833             trans.checkpoint(to.user+"=>"+to.delegate, Env.ALWAYS);
834
835             return Result.ok(to);
836         } catch (Exception t) {
837             return Result.err(Status.ERR_BadData,t.getMessage());
838         }
839     }
840
841     /*
842      * We want "Expired" dates to start at a specified time set by the Organization, and consistent wherever
843      * the date is created from.
844      */ 
845     private Date getExpires(Organization org, Expiration exp, Request base, String id) {
846         XMLGregorianCalendar end = base.getEnd();
847         GregorianCalendar gc = end==null?new GregorianCalendar():end.toGregorianCalendar();
848         GregorianCalendar orggc;
849         orggc = org.expiration(gc,exp,id); 
850
851         // We'll choose the lesser of dates to ensure Policy Compliance...
852     
853         GregorianCalendar endgc = end==null||gc.after(orggc)?orggc:gc;
854         // Allow the Organization to determine when official "day Start" begins, Specifically when to consider something Expired.
855         endgc = Chrono.firstMomentOfDay(endgc);
856         endgc.set(GregorianCalendar.HOUR_OF_DAY, org.startOfDay());
857         return endgc.getTime();
858     }
859
860
861     @Override
862     public Result<Keys> keys(Collection<String> from) {
863         Keys keys = new Keys();
864         keys.getKey().addAll(from);
865         return Result.ok(keys).emptyList(from.isEmpty());
866     }
867
868 }