1 package com.att.authz.gui.pages;
3 import java.io.IOException;
4 import java.net.ConnectException;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.Comparator;
9 import com.att.authz.env.AuthzTrans;
10 import com.att.authz.gui.AuthGUI;
11 import com.att.authz.gui.BreadCrumbs;
12 import com.att.authz.gui.NamedCode;
13 import com.att.authz.gui.Page;
14 import com.att.authz.gui.Table;
15 import com.att.authz.gui.Table.Cells;
16 import com.att.authz.gui.table.AbsCell;
17 import com.att.authz.gui.table.TextCell;
18 import com.att.cadi.CadiException;
19 import com.att.cadi.Symm;
20 import com.att.cadi.client.Future;
21 import com.att.cadi.client.Rcli;
22 import com.att.cadi.client.Retryable;
23 import com.att.cssa.rserv.HttpMethods;
24 import com.att.inno.env.APIException;
25 import com.att.inno.env.Env;
26 import com.att.inno.env.TimeTaken;
27 import com.att.xgen.Cache;
28 import com.att.xgen.html.HTMLGen;
31 import aaf.v2_0.Api.Route;
33 public class ApiDocs extends Page {
35 private static final String HREF = "/gui/api";
36 private static final String NAME = "AAF RESTful API";
37 private static final String fields[] = {};
38 private static final String ERROR_LINK = "<a href=\"./example/"
39 + "YXBwbGljYXRpb24vRXJyb3IranNvbg=="
40 // + Symm.base64noSplit().encode("application/Error+json")
42 + "<a href=\"./example/"
43 + "YXBwbGljYXRpb24vRXJyb3IreG1s"
44 // + Symm.base64noSplit().encode("application/Error+xml")
48 public ApiDocs(final AuthGUI gui, final Page ... breadcrumbs) throws APIException, IOException {
49 super(gui.env,NAME,HREF, fields,
50 new BreadCrumbs(breadcrumbs),
52 new Table<AuthGUI,AuthzTrans>("AAF API Reference",gui.env.newTransNoAvg(),new Model(), "class=std")
56 private static class Preamble extends NamedCode {
58 private static final String I = "i";
61 super(false, "preamble");
65 public void code(Cache<HTMLGen> cache, HTMLGen xgen) throws APIException, IOException {
66 xgen.leaf(HTMLGen.H1).text("AAF 2.0 RESTful interface").end()
68 xgen.leaf(HTMLGen.H2).text("Accessing RESTful").end();
70 .leaf(HTMLGen.LI).text("AAF RESTful service is secured by the following:").end()
72 .leaf(HTMLGen.LI).text("The Client must utilize HTTP/S. Non Secure HTTP is not acceptable").end()
73 .leaf(HTMLGen.LI).text("The Client MUST supply an Identity validated by one of the following mechanisms").end()
75 .leaf(HTMLGen.LI).text("(Near Future) Application level Certificate").end()
78 .leaf(HTMLGen.LI).text("Responses").end()
80 .leaf(HTMLGen.LI).text("Each API Entity listed shows what structure will be accepted by service (ContentType) "
81 + "or responded with by service (Accept). Therefore, use these in making your call. Critical for PUT/POST.").end()
82 .leaf(HTMLGen.LI).text("Each API call may respond with JSON or XML. Choose the ContentType/Accept that has "
83 + "+json after the type for JSON or +xml after the Type for XML").end()
84 .leaf(HTMLGen.LI).text("XSDs for Versions").end()
86 .leaf(HTMLGen.LI).leaf(HTMLGen.A,"href=../theme/aaf_2_0.xsd").text("API 2.0").end().end()
88 .leaf(HTMLGen.LI).text("AAF can support multiple Versions of the API. Choose the ContentType/Accept that has "
89 + "the appropriate version=?.?").end()
90 .leaf(HTMLGen.LI).text("All Errors coming from AAF return AT&T Standard Error Message as a String: " + ERROR_LINK
91 + " (does not apply to errors from Container)").end()
93 .leaf(HTMLGen.LI).text("Character Restrictions").end()
95 .leaf(HTMLGen.LI).text("Character Restrictions must depend on the Enforcement Point used").end()
96 .leaf(HTMLGen.LI).text("Most AAF usage will be AAF Enforcement Point Characters for Instance and Action are:")
97 .br().br().leaf(I).text("a-zA-Z0-9,.()_-=%").end()
98 .br().br().text("For Instance, you may declare a multi-dimensional key with : (colon) separator, example:").end()
99 .br().leaf(I).text(":myCluster:myKeyspace").end()
100 .br().br().text("The * (asterix) may be used as a wild-card by itself or within the multi-dimensional key, example:")
101 .br().leaf(I).text(":myCluster:*").end()
102 .br().br().text("The % (percent) character can be used as an Escape Character. Applications can use % followed by 2 hexadecimal "
103 + "digits to cover odd keys. It is their code, however, which must translate.")
104 .br().br().text("The = (equals) is allowed so that Applications can pass Base64 encodations of binary keys").end()
105 .leaf(HTMLGen.LI).text("Ask for a Consultation on how these are typically used, or, if your tool is the only Enforcement Point, if set may be expanded").end()
110 The Content is defined in the AAF XSD - TODO Add aaf.xsd�;
111 Character Restrictions
113 URLs impose restrictions on characters which have specific meanings. This means you cannot have these characters in the Field Content you send
114 “#� is a “Fragment URL�, or anchor. Content after this Character is not sent. AAF cannot do anything about this… don’t use it.
115 “?=&�. These are used to delineate Parameters.
116 “/“ is used to separate fields
122 * Implement the Table Content for Permissions by User
126 private static class Model implements Table.Data<AuthGUI,AuthzTrans> {
127 public static final String[] HEADERS = new String[] {"Entity","Method","Path Info","Description"};
128 private static final TextCell BLANK = new TextCell("");
131 public String[] headers() {
135 @SuppressWarnings("unchecked")
137 public Cells get(final AuthGUI gui, final AuthzTrans trans) {
138 ArrayList<AbsCell[]> ns = new ArrayList<AbsCell[]>();
139 ArrayList<AbsCell[]> perms = new ArrayList<AbsCell[]>();
140 ArrayList<AbsCell[]> roles = new ArrayList<AbsCell[]>();
141 ArrayList<AbsCell[]> user = new ArrayList<AbsCell[]>();
142 ArrayList<AbsCell[]> aafOnly = new ArrayList<AbsCell[]>();
143 ArrayList<AbsCell[]> rv = new ArrayList<AbsCell[]>();
146 TimeTaken tt = trans.start("AAF APIs",Env.REMOTE);
148 gui.clientAsUser(trans.getUserPrincipal(), new Retryable<Void>() {
150 public Void code(Rcli<?> client) throws CadiException, ConnectException, APIException {
151 Future<Api> fa = client.read("/api",gui.apiDF);
154 TimeTaken tt2 = trans.start("Load Data", Env.SUB);
156 if(fa.value!=null)for(Route r : fa.value.getRoute()) {
157 String path = r.getPath();
159 StringBuilder desc = new StringBuilder();
161 desc.append("<p class=double>");
162 desc.append(r.getDesc());
164 if(r.getComments().size()>0) {
165 for(String ct : r.getComments()) {
166 desc.append("</p><p class=api_comment>");
171 if(r.getParam().size()>0) {
172 desc.append("<hr><p class=api_label>Parameters</p>");
174 for(String params : r.getParam()) {
175 String param[] = params.split("\\s*\\|\\s*");
176 desc.append("</p><p class=api_contentType>");
177 desc.append(param[0]);
179 desc.append(param[1]);
180 if("true".equalsIgnoreCase(param[2])) {
181 desc.append(" (Required)");
187 if(r.getExpected()!=0) {
188 desc.append("</p><p class=api_label>Expected HTTP Code</p><p class=api_comment>");
189 desc.append(r.getExpected());
192 if(r.getExplicitErr().size()!=0) {
193 desc.append("</p><p class=api_label>Explicit HTTP Error Codes</p><p class=api_comment>");
194 boolean first = true;
195 for(int ee : r.getExplicitErr()) {
205 desc.append("</p><p class=api_label>");
206 desc.append("GET".equals(r.getMeth())?"Accept:":"ContentType:");
207 Collections.sort(r.getContentType());
208 if(r.getPath().startsWith("/authn/basicAuth")) {
209 desc.append("</p><p class=api_contentType>text/plain");
211 for(String ct : r.getContentType()) {
212 if(ct.contains("version=2")) {
213 desc.append("</p><p class=api_contentType><a href=\"./example/");
215 desc.append(Symm.base64noSplit.encode(ct));
216 } catch (IOException e) {
217 throw new CadiException(e);
227 AbsCell[] sa = new AbsCell[] {
229 new TextCell(r.getMeth(),"class=right"),
230 new TextCell(r.getPath()),
231 new TextCell(desc.toString()),
234 if(path.startsWith("/authz/perm")) {
235 sa[0] = perms.size()==0?new TextCell("PERMISSION"):BLANK;
237 } else if(path.startsWith("/authz/role") || path.startsWith("/authz/userRole")) {
238 sa[0] = roles.size()==0?new TextCell("ROLE"):BLANK;
240 } else if(path.startsWith("/authz/ns")) {
241 sa[0] = ns.size()==0?new TextCell("NAMESPACE"):BLANK;
243 } else if(path.startsWith("/authn/basicAuth")
244 || path.startsWith("/authn/validate")
245 || path.startsWith("/authz/user")) {
246 sa[0] = user.size()==0?new TextCell("USER"):BLANK;
249 sa[0] = aafOnly.size()==0?new TextCell("AAF ONLY"):BLANK;
253 //TODO if(trans.fish(p))
254 prepare(rv, perms,roles,ns,user);
259 gui.writeError(trans, fa, null);
264 } catch (Exception e) {
265 trans.error().log(e.getMessage());
270 return new Cells(rv,null);
273 @SuppressWarnings("unchecked")
274 private void prepare(ArrayList<AbsCell[]> rv, ArrayList<AbsCell[]> ... all) {
277 for(ArrayList<AbsCell[]> al : all) {
282 al.get(0).clone()[0]=BLANK;
283 Collections.sort(al, new Comparator<AbsCell[]>() {
285 public int compare(AbsCell[] ca1, AbsCell[] ca2) {
286 int meth = ((TextCell)ca1[2]).name.compareTo(
287 ((TextCell)ca2[2]).name);
289 return (HttpMethods.valueOf(((TextCell)ca1[1]).name).compareTo(
290 HttpMethods.valueOf(((TextCell)ca2[1]).name)));