Changes from Onsite Tests
[aaf/authz.git] / auth / auth-gui / src / main / java / org / onap / aaf / auth / gui / pages / ApiDocs.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.gui.pages;
23
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;
29
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;
52
53 import aaf.v2_0.Api;
54 import aaf.v2_0.Api.Route;
55
56 public class ApiDocs extends Page {
57     // Package on purpose
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") 
64             + "\">JSON</a> "
65             + "<a href=\"./example/"
66             + "YXBwbGljYXRpb24vRXJyb3IreG1s"
67 //            + Symm.base64noSplit().encode("application/Error+xml") 
68             + "\">XML</a> ";
69
70
71     public ApiDocs(final AAF_GUI gui, final Page ... breadcrumbs) throws APIException, IOException {
72         super(gui.env,NAME,HREF, fields,
73             new BreadCrumbs(breadcrumbs),
74             new Preamble(gui),
75             new Table<AAF_GUI,AuthzTrans>("AAF API Reference",gui.env.newTransNoAvg(),new Model(), "class=std")
76             );
77     }
78
79     private static class Preamble extends NamedCode {
80
81         private static final String I = "i";
82         private final String fsUrl;
83
84         public Preamble(AAF_GUI gui) {
85             super(false, "preamble");
86             fsUrl = gui.access.getProperty(Config.AAF_URL_FS, "/theme");
87         }
88
89         @Override
90         public void code(Cache<HTMLGen> cache, HTMLGen xgen) throws APIException, IOException {
91             xgen.leaf(HTMLGen.H1).text("AAF 2.0 RESTful interface").end()
92                 .hr();
93             xgen.leaf(HTMLGen.H2).text("Accessing RESTful").end();
94             xgen.incr(HTMLGen.UL)
95                     .leaf(HTMLGen.LI).text("AAF RESTful service is secured by the following:").end()
96                     .incr(HTMLGen.UL)
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()
99                         .incr(HTMLGen.UL)
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()
102                         .end()
103                     .end()
104                     .leaf(HTMLGen.LI).text("Responses").end()
105                     .incr(HTMLGen.UL)
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()
111                         .incr(HTMLGen.UL)
112                             .leaf(HTMLGen.LI).leaf(HTMLGen.A,"href=" + fsUrl + "/aaf_2_0.xsd").text("API 2.0").end().end()
113                         .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()
118                     .end()
119                     .leaf(HTMLGen.LI).text("Character Restrictions").end()
120                     .incr(HTMLGen.UL)
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()
132                     .end()
133                 .end();
134             /*
135
136             The Content is defined in the AAF XSD - TODO Add aaf.xsd”;
137             Character Restrictions
138
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
143             */
144         }
145
146     };
147     /**
148      * Implement the Table Content for Permissions by User
149      *
150      * @author Jonathan
151      *
152      */
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("");
156
157         @Override
158         public String[] headers() {
159             return HEADERS;
160         }
161
162
163         @Override
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<>();
171
172
173             final TimeTaken tt = trans.start("AAF APIs",Env.REMOTE);
174             try {
175                 gui.clientAsUser(trans.getUserPrincipal(), new Retryable<Void>() {
176                     @SuppressWarnings("unchecked")
177                     @Override
178                     public Void code(Rcli<?> client) throws CadiException, ConnectException, APIException {
179                         Future<Api> fa = client.read("/api",gui.getDF(Api.class));
180                         if (fa.get(5000)) {
181                             tt.done();
182                             TimeTaken tt2 = trans.start("Load Data", Env.SUB);
183                             try {
184                                 if (fa.value!=null)for (Route r : fa.value.getRoute()) {
185                                     String path = r.getPath();
186                                     // Build info
187                                     StringBuilder desc = new StringBuilder();
188
189                                     desc.append("<p class=double>");
190                                     desc.append(r.getDesc());
191
192                                     if (!r.getComments().isEmpty()) {
193                                         for (String ct : r.getComments()) {
194                                             desc.append("</p><p class=api_comment>");
195                                             desc.append(ct);
196                                         }
197                                     }
198
199                                     if (!r.getParam().isEmpty()) {
200                                         desc.append("<hr><p class=api_label>Parameters</p>");
201
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]);
206                                             desc.append(" : ");
207                                             desc.append(param[1]);
208                                             if ("true".equalsIgnoreCase(param[2])) {
209                                                 desc.append(" (Required)");
210                                             }
211                                         }
212                                     }
213
214
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());
218                                     }
219
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()) {
224                                             if (first) {
225                                                 first = false;
226                                             } else {
227                                                 desc.append(", ");
228                                             }
229                                             desc.append(ee);
230                                         }
231                                     }
232
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");
238                                     }
239                                     for (String ct : r.getContentType()) {
240                                         if (ct.contains("version=2")) {
241                                             desc.append("</p><p class=api_contentType><a href=\"./example/");
242                                             try {
243                                                 desc.append(Symm.base64noSplit.encode(ct));
244                                             } catch (IOException e) {
245                                                 throw new CadiException(e);
246                                             }
247                                             desc.append("\"/>");
248                                             desc.append(ct);
249                                             desc.append("</a>");
250                                         }
251                                     }
252                                     desc.append("</p>");
253
254
255                                     AbsCell[] sa = new AbsCell[] {
256                                         null,
257                                         new TextCell(r.getMeth(),"class=right"),
258                                         new TextCell(r.getPath()),
259                                         new TextCell(desc.toString()),
260                                     };
261
262                                     if (path.startsWith("/authz/perm")) {
263                                         sa[0] = perms.isEmpty()?new TextCell("PERMISSION"):BLANK;
264                                         perms.add(sa);
265                                     } else if (path.startsWith("/authz/role") || path.startsWith("/authz/userRole")) {
266                                         sa[0] = roles.isEmpty()?new TextCell("ROLE"):BLANK;
267                                         roles.add(sa);
268                                     } else if (path.startsWith("/authz/ns")) {
269                                         sa[0] = ns.isEmpty()?new TextCell("NAMESPACE"):BLANK;
270                                         ns.add(sa);
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;
275                                         user.add(sa);
276                                     } else {
277                                         sa[0] = aafOnly.isEmpty()?new TextCell("AAF ONLY"):BLANK;
278                                         aafOnly.add(sa);
279                                     }
280                                 }
281                                 //TODO if (trans.fish(p))
282                                 prepare(rv, perms,roles,ns,user);
283                             } finally {
284                                 tt2.done();
285                             }
286                         } else {
287                             gui.writeError(trans, fa, null, 0);
288                         }
289                         return null;
290                     }
291                 });
292             } catch (Exception e) {
293                 trans.error().log(e.getMessage());
294             } finally {
295                 tt.done();
296             }
297
298             return new Cells(rv,null);
299         }
300
301         @SuppressWarnings("unchecked")
302         private void prepare(ArrayList<AbsCell[]> rv, ArrayList<AbsCell[]> ... all) {
303             AbsCell lead;
304             AbsCell[] row;
305             for (ArrayList<AbsCell[]> al : all) {
306                 if (al.size()>1) {
307                     row = al.get(0);
308                     lead = row[0];
309                     row[0]=BLANK;
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);
314                         if (meth == 0) {
315                             return (HttpMethods.valueOf(((TextCell)ca1[1]).name).compareTo(
316                                 HttpMethods.valueOf(((TextCell)ca2[1]).name)));
317                         } else {
318                             return meth;
319                         }
320                     });
321                     // set new first row
322                     al.get(0)[0]=lead;
323
324                     rv.addAll(al);
325                 }
326             }
327         }
328     }
329 }