2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.auth.gui.pages;
24 import java.io.IOException;
25 import java.net.ConnectException;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Comparator;
30 import org.onap.aaf.auth.env.AuthzTrans;
31 import org.onap.aaf.auth.gui.AAF_GUI;
32 import org.onap.aaf.auth.gui.BreadCrumbs;
33 import org.onap.aaf.auth.gui.NamedCode;
34 import org.onap.aaf.auth.gui.Page;
35 import org.onap.aaf.auth.gui.Table;
36 import org.onap.aaf.auth.gui.Table.Cells;
37 import org.onap.aaf.auth.gui.table.AbsCell;
38 import org.onap.aaf.auth.gui.table.TableData;
39 import org.onap.aaf.auth.gui.table.TextCell;
40 import org.onap.aaf.auth.rserv.HttpMethods;
41 import org.onap.aaf.cadi.CadiException;
42 import org.onap.aaf.cadi.Symm;
43 import org.onap.aaf.cadi.client.Future;
44 import org.onap.aaf.cadi.client.Rcli;
45 import org.onap.aaf.cadi.client.Retryable;
46 import org.onap.aaf.cadi.config.Config;
47 import org.onap.aaf.misc.env.APIException;
48 import org.onap.aaf.misc.env.Env;
49 import org.onap.aaf.misc.env.TimeTaken;
50 import org.onap.aaf.misc.xgen.Cache;
51 import org.onap.aaf.misc.xgen.html.HTMLGen;
54 import aaf.v2_0.Api.Route;
56 public class ApiDocs extends Page {
58 private static final String HREF = "/gui/api";
59 private static final String NAME = "AAF RESTful API";
60 private static final String fields[] = {};
61 private static final String ERROR_LINK = "<a href=\"./example/"
62 + "YXBwbGljYXRpb24vRXJyb3IranNvbg=="
63 // + Symm.base64noSplit().encode("application/Error+json")
65 + "<a href=\"./example/"
66 + "YXBwbGljYXRpb24vRXJyb3IreG1s"
67 // + Symm.base64noSplit().encode("application/Error+xml")
71 public ApiDocs(final AAF_GUI gui, final Page ... breadcrumbs) throws APIException, IOException {
72 super(gui.env,NAME,HREF, fields,
73 new BreadCrumbs(breadcrumbs),
75 new Table<AAF_GUI,AuthzTrans>("AAF API Reference",gui.env.newTransNoAvg(),new Model(), "class=std")
79 private static class Preamble extends NamedCode {
81 private static final String I = "i";
82 private final String fsUrl;
84 public Preamble(AAF_GUI gui) {
85 super(false, "preamble");
86 fsUrl = gui.access.getProperty(Config.AAF_URL_FS, "/theme");
90 public void code(Cache<HTMLGen> cache, HTMLGen xgen) throws APIException, IOException {
91 xgen.leaf(HTMLGen.H1).text("AAF 2.0 RESTful interface").end()
93 xgen.leaf(HTMLGen.H2).text("Accessing RESTful").end();
95 .leaf(HTMLGen.LI).text("AAF RESTful service is secured by the following:").end()
97 .leaf(HTMLGen.LI).text("The Client must utilize HTTP/S. Non Secure HTTP is not acceptable").end()
98 .leaf(HTMLGen.LI).text("The Client MUST supply an Identity validated by one of the following mechanisms").end()
100 .leaf(HTMLGen.LI).text("BASIC AUTH protocol using Organization Registered AppID, provisioned in AAF").end()
101 .leaf(HTMLGen.LI).text("(Near Future) Application level Certificate").end()
104 .leaf(HTMLGen.LI).text("Responses").end()
106 .leaf(HTMLGen.LI).text("Each API Entity listed shows what structure will be accepted by service (ContentType) "
107 + "or responded with by service (Accept). Therefore, use these in making your call. Critical for PUT/POST.").end()
108 .leaf(HTMLGen.LI).text("Each API call may respond with JSON or XML. Choose the ContentType/Accept that has "
109 + "+json after the type for JSON or +xml after the Type for XML").end()
110 .leaf(HTMLGen.LI).text("XSDs for Versions").end()
112 .leaf(HTMLGen.LI).leaf(HTMLGen.A,"href=" + fsUrl + "/aaf_2_0.xsd").text("API 2.0").end().end()
114 .leaf(HTMLGen.LI).text("AAF can support multiple Versions of the API. Choose the ContentType/Accept that has "
115 + "the appropriate version=?.?").end()
116 .leaf(HTMLGen.LI).text("All Errors coming from AAF return AT&T Standard Error Message as a String: " + ERROR_LINK
117 + " (does not apply to errors from Container)").end()
119 .leaf(HTMLGen.LI).text("Character Restrictions").end()
121 .leaf(HTMLGen.LI).text("Character Restrictions must depend on the Enforcement Point used").end()
122 .leaf(HTMLGen.LI).text("Most AAF usage will be AAF Enforcement Point Characters for Instance and Action are:")
123 .br().br().leaf(I).text("a-zA-Z0-9,.()_-=%").end()
124 .br().br().text("For Instance, you may declare a multi-dimensional key with : (colon) separator, example:").end()
125 .br().leaf(I).text(":myCluster:myKeyspace").end()
126 .br().br().text("The * (asterix) may be used as a wild-card by itself or within the multi-dimensional key, example:")
127 .br().leaf(I).text(":myCluster:*").end()
128 .br().br().text("The % (percent) character can be used as an Escape Character. Applications can use % followed by 2 hexadecimal "
129 + "digits to cover odd keys. It is their code, however, which must translate.")
130 .br().br().text("The = (equals) is allowed so that Applications can pass Base64 encodations of binary keys").end()
131 .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()
136 The Content is defined in the AAF XSD - TODO Add aaf.xsd”;
137 Character Restrictions
139 URLs impose restrictions on characters which have specific meanings. This means you cannot have these characters in the Field Content you send
140 “#” is a “Fragment URL”, or anchor. Content after this Character is not sent. AAF cannot do anything about this… don’t use it.
141 “?=&”. These are used to delineate Parameters.
142 “/“ is used to separate fields
148 * Implement the Table Content for Permissions by User
153 private static class Model extends TableData<AAF_GUI,AuthzTrans> {
154 public static final String[] HEADERS = new String[] {"Entity","Method","Path Info","Description"};
155 private static final TextCell BLANK = new TextCell("");
158 public String[] headers() {
164 public Cells get(final AuthzTrans trans, final AAF_GUI gui) {
165 final ArrayList<AbsCell[]> ns = new ArrayList<>();
166 final ArrayList<AbsCell[]> perms = new ArrayList<>();
167 final ArrayList<AbsCell[]> roles = new ArrayList<>();
168 final ArrayList<AbsCell[]> user = new ArrayList<>();
169 final ArrayList<AbsCell[]> aafOnly = new ArrayList<>();
170 final ArrayList<AbsCell[]> rv = new ArrayList<>();
173 final TimeTaken tt = trans.start("AAF APIs",Env.REMOTE);
175 gui.clientAsUser(trans.getUserPrincipal(), new Retryable<Void>() {
176 @SuppressWarnings("unchecked")
178 public Void code(Rcli<?> client) throws CadiException, ConnectException, APIException {
179 Future<Api> fa = client.read("/api",gui.getDF(Api.class));
182 TimeTaken tt2 = trans.start("Load Data", Env.SUB);
184 if (fa.value!=null)for (Route r : fa.value.getRoute()) {
185 String path = r.getPath();
187 StringBuilder desc = new StringBuilder();
189 desc.append("<p class=double>");
190 desc.append(r.getDesc());
192 if (!r.getComments().isEmpty()) {
193 for (String ct : r.getComments()) {
194 desc.append("</p><p class=api_comment>");
199 if (!r.getParam().isEmpty()) {
200 desc.append("<hr><p class=api_label>Parameters</p>");
202 for (String params : r.getParam()) {
203 String param[] = params.split("\\s*\\|\\s*");
204 desc.append("</p><p class=api_contentType>");
205 desc.append(param[0]);
207 desc.append(param[1]);
208 if ("true".equalsIgnoreCase(param[2])) {
209 desc.append(" (Required)");
215 if (r.getExpected()!=0) {
216 desc.append("</p><p class=api_label>Expected HTTP Code</p><p class=api_comment>");
217 desc.append(r.getExpected());
220 if (!r.getExplicitErr().isEmpty()) {
221 desc.append("</p><p class=api_label>Explicit HTTP Error Codes</p><p class=api_comment>");
222 boolean first = true;
223 for (int ee : r.getExplicitErr()) {
233 desc.append("</p><p class=api_label>");
234 desc.append("GET".equals(r.getMeth())?"Accept:":"ContentType:");
235 Collections.sort(r.getContentType());
236 if (r.getPath().startsWith("/authn/basicAuth")) {
237 desc.append("</p><p class=api_contentType>text/plain");
239 for (String ct : r.getContentType()) {
240 if (ct.contains("version=2")) {
241 desc.append("</p><p class=api_contentType><a href=\"./example/");
243 desc.append(Symm.base64noSplit.encode(ct));
244 } catch (IOException e) {
245 throw new CadiException(e);
255 AbsCell[] sa = new AbsCell[] {
257 new TextCell(r.getMeth(),"class=right"),
258 new TextCell(r.getPath()),
259 new TextCell(desc.toString()),
262 if (path.startsWith("/authz/perm")) {
263 sa[0] = perms.isEmpty()?new TextCell("PERMISSION"):BLANK;
265 } else if (path.startsWith("/authz/role") || path.startsWith("/authz/userRole")) {
266 sa[0] = roles.isEmpty()?new TextCell("ROLE"):BLANK;
268 } else if (path.startsWith("/authz/ns")) {
269 sa[0] = ns.isEmpty()?new TextCell("NAMESPACE"):BLANK;
271 } else if (path.startsWith("/authn/basicAuth")
272 || path.startsWith("/authn/validate")
273 || path.startsWith("/authz/user")) {
274 sa[0] = user.isEmpty()?new TextCell("USER"):BLANK;
277 sa[0] = aafOnly.isEmpty()?new TextCell("AAF ONLY"):BLANK;
281 //TODO if (trans.fish(p))
282 prepare(rv, perms,roles,ns,user);
287 gui.writeError(trans, fa, null, 0);
292 } catch (Exception e) {
293 trans.error().log(e.getMessage());
298 return new Cells(rv,null);
301 @SuppressWarnings("unchecked")
302 private void prepare(ArrayList<AbsCell[]> rv, ArrayList<AbsCell[]> ... all) {
305 for (ArrayList<AbsCell[]> al : all) {
310 al.get(0).clone()[0]=BLANK;
311 Collections.sort(al, (ca1, ca2) -> {
312 int meth = ((TextCell)ca1[2]).name.compareTo(
313 ((TextCell)ca2[2]).name);
315 return (HttpMethods.valueOf(((TextCell)ca1[1]).name).compareTo(
316 HttpMethods.valueOf(((TextCell)ca2[1]).name)));