sg481n | 02017aa | 2017-09-26 15:18:31 +0000 | [diff] [blame] | 1 | =================
|
| 2 | Connecting to AAF
|
| 3 | =================
|
| 4 |
|
| 5 | Methods to Connect
|
| 6 | ==================
|
| 7 |
|
| 8 | If you are a Servlet in a Container, use CADI Framework with AAF Plugin. It's very easy, and includes BasicAuth for Services.
|
| 9 | Java Technologies
|
| 10 | Technologies using Servlet Filters
|
| 11 | DME2 (and other Servlet Containers) can use Servlet Filters
|
| 12 | Any WebApp can plug in CADI as a Servlet Filter
|
| 13 | Jetty can attach a Servlet Filter with Code, or as WebApp
|
| 14 | Tomcat 7 has a "Valve" plugin, which is similar and supported
|
| 15 | Use the AAFLur Code directly (shown)
|
| 16 | All Java Technologies utilize Configuration to set what Security elements are required
|
| 17 | example: Global Login can be turned on/off, AAF Client needs information to connect to AAF Service
|
| 18 | There are several specialty cases, which AAF can work with, including embedding all properties in a Web.xml, but the essentials needed are:
|
| 19 | CADI Jars
|
| 20 | cadi.properties file (configured the same for all technologies)
|
| 21 | Encrypt passwords with included CADI technology, so that there are no Clear Text Passwords in Config Files (ASPR)
|
| 22 | See CADI Deployment on how to perform this with several different technologies.
|
| 23 | AAF Restfully (see RESTFul APIS)
|
| 24 |
|
| 25 | IMPORTANT: If Direct RESTFul API is used, then it is the Client's responsibility to Cache and avoid making an AAF Service Calls too often
|
| 26 | Example: A Tool like Cassandra will ask for Authentication hundreds of times a second for the same identity during a transaction. Calling the AAF Service for each would be slow for the client, and wasteful of Network and AAF Service Capacities.
|
| 27 | Rogue Clients can and will be denied access to AAF.
|
| 28 |
|
| 29 |
|
| 30 | J2EE (Servlet Filter) Method
|
| 31 | ============================
|
| 32 |
|
| 33 | 1. Per J2EE design, the Filter will deny any unauthenticated HTTP/S call; the Servlet will not even be invoked.
|
| 34 | a. Therefore, the Servlet can depend on any transaction making it to their code set is Authenticated.
|
| 35 | b. Identity can be viewed based on the HttpServletRequest Object (request.getUserPrincipal() )
|
| 36 | 2. Per J2EE design, AAF Filter overloads the HttpServletRequest for a String related to "Role". (request.isUserInRole("...") )
|
| 37 | a. For AAF, do not put in "Role", but the three parts of requested "Permission", separated by "|", i.e. "org.onap.aaf.myapp.myperm|myInstance|myAction".
|
| 38 | 3. NOT REQUIRED: An added benefit, but not required, is a JASPI like interface, where you can add an Annotation to your Servlet.
|
| 39 | a. When used, no transaction will come into your code if the listed Permissions are not Granted to the Incoming Transaction.
|
| 40 | b. This might be helpful for covering separate Management Servlet implementations.
|
| 41 |
|
| 42 |
|
| 43 |
|
| 44 | Servlet Code Snippet
|
| 45 | =========================
|
| 46 |
|
| 47 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
| 48 | HttpServletRequest request;
|
| 49 | try {
|
| 50 | request = (HttpServletRequest)req;
|
| 51 | } catch (ClassCastException e) {
|
| 52 | throw new ServletException("Only serving HTTP today",e);
|
| 53 | }
|
| 54 |
|
| 55 | // Note: CADI is OVERLOADING the concept of "isUserInRole".. You need to think "doesUserHavePermssion()"
|
| 56 | // Assume that you have CREATED and GRANTED An AAF Permission in YOUR Namespace
|
| 57 | // Example Permission: "org.onap.aaf.myapp.myPerm * write"
|
| 58 |
|
| 59 | // Think in your head, "Does user have write permission on any instance of org.onap.aaf.myapp.myPerm
|
| 60 | if(request.isUserInRole("org.onap.aaf.myapp.myPerm|*|write")) {
|
| 61 | // *** Do something here that someone with "myPerm write" permissions is allowed to do
|
| 62 | } else {
|
| 63 | // *** Do something reasonable if user is denied, like an Error Message
|
| 64 | }
|
| 65 |
|
| 66 | }
|
| 67 |
|
| 68 | Here is a working TestServlet, where you can play with different Permissions that you own on the URL, i.e.:
|
| 69 | https://<your machine:port>/caditest/testme?PERM=org.onap.aaf.myapp.myPerm|*|write
|
| 70 |
|
| 71 | Sample Servlet (Working example)
|
| 72 | ================================
|
| 73 | package org.onap.aaf.cadi.debug;
|
| 74 | import java.io.FileInputStream;
|
| 75 | import java.io.IOException;
|
| 76 | import java.net.InetAddress;
|
| 77 | import java.net.UnknownHostException;
|
| 78 | import java.util.HashMap;
|
| 79 | import java.util.Map;
|
| 80 | import java.util.Map.Entry;
|
| 81 | import java.util.Properties;
|
| 82 | import javax.servlet.Servlet;
|
| 83 | import javax.servlet.ServletConfig;
|
| 84 | import javax.servlet.ServletException;
|
| 85 | import javax.servlet.ServletRequest;
|
| 86 | import javax.servlet.ServletResponse;
|
| 87 | import javax.servlet.http.HttpServletRequest;
|
| 88 | import org.eclipse.jetty.server.Server;
|
| 89 | import org.eclipse.jetty.server.ServerConnector;
|
| 90 | import org.eclipse.jetty.server.handler.ContextHandler;
|
| 91 | import org.eclipse.jetty.servlet.FilterHolder;
|
| 92 | import org.eclipse.jetty.servlet.FilterMapping;
|
| 93 | import org.eclipse.jetty.servlet.ServletContextHandler;
|
| 94 | import org.eclipse.jetty.servlet.ServletHandler;
|
| 95 | import org.onap.aaf.cadi.filter.CadiFilter;
|
| 96 | import org.onap.aaf.cadi.filter.RolesAllowed;
|
| 97 | import org.onap.aaf.cadi.jetty.MiniJASPIWrap;
|
| 98 |
|
| 99 | public class CSPServletTest {
|
| 100 | public static void main(String[] args) {
|
| 101 | // Go ahead and print Test reports in cadi-core first
|
| 102 | Test.main(args);
|
| 103 | String hostname=null;
|
| 104 | try {
|
| 105 | hostname = InetAddress.getLocalHost().getHostName();
|
| 106 | } catch (UnknownHostException e) {
|
| 107 | e.printStackTrace();
|
| 108 | System.exit(1);
|
| 109 | }
|
| 110 | Properties props = new Properties();
|
| 111 | Map<String,String> map = new HashMap<String,String>();
|
| 112 | try {
|
| 113 | FileInputStream fis = new FileInputStream("run/cadi.properties");
|
| 114 | try {
|
| 115 | props.load(fis);
|
| 116 | String key,value;
|
| 117 | for( Entry<Object, Object> es : props.entrySet()) {
|
| 118 | key = es.getKey().toString();
|
| 119 | value = es.getValue().toString();
|
| 120 | map.put(key,value);
|
| 121 | if(key.startsWith("AFT_") || key.startsWith("DME2")) {
|
| 122 | System.setProperty(key,value);
|
| 123 | }
|
| 124 | }
|
| 125 | } finally {
|
| 126 | fis.close();
|
| 127 | }
|
| 128 | } catch(IOException e) {
|
| 129 | System.err.println("Cannot load run/cadi.properties");
|
| 130 | System.exit(1);
|
| 131 | }
|
| 132 | String portStr = System.getProperty("port");
|
| 133 | int port = portStr==null?8080:Integer.parseInt(portStr);
|
| 134 | try {
|
| 135 | // Add ServletHolder(s) and Filter(s) to a ServletHandler
|
| 136 | ServletHandler shand = new ServletHandler();
|
| 137 |
|
| 138 | FilterHolder cfh = new FilterHolder(CadiFilter.class);
|
| 139 | cfh.setInitParameters(map);
|
| 140 |
|
| 141 | shand.addFilterWithMapping(cfh, "/*", FilterMapping.ALL);
|
| 142 | shand.addServletWithMapping(new MiniJASPIWrap(MyServlet.class),"/*");
|
| 143 | // call initialize after start
|
| 144 |
|
| 145 | ContextHandler ch = new ServletContextHandler();
|
| 146 | ch.setContextPath("/caditest");
|
| 147 | ch.setHandler(shand);
|
| 148 | for( Entry<Object,Object> es : props.entrySet()) {
|
| 149 | ch.getInitParams().put(es.getKey().toString(), es.getValue().toString());
|
| 150 | }
|
| 151 | //ch.setErrorHandler(new MyErrorHandler());
|
| 152 |
|
| 153 | // Create Server and Add Context Handler
|
| 154 | final Server server = new Server();
|
| 155 | ServerConnector http = new ServerConnector(server);
|
| 156 | http.setPort(port);
|
| 157 | server.addConnector(http);
|
| 158 | server.setHandler(ch);
|
| 159 |
|
| 160 | // Start
|
| 161 | server.start();
|
| 162 | shand.initialize();
|
| 163 |
|
| 164 | System.out.println("To test, put http://"+ hostname + ':' + port + "/caditest/testme in a browser or 'curl'");
|
| 165 | // if we were really a server, we'd block the main thread with this join...
|
| 166 | // server.join();
|
| 167 | // But... since we're a test service, we'll block on StdIn
|
| 168 | System.out.println("Press <Return> to end service...");
|
| 169 | System.in.read();
|
| 170 | server.stop();
|
| 171 | System.out.println("All done, have a good day!");
|
| 172 | } catch (Exception e) {
|
| 173 | e.printStackTrace();
|
| 174 | System.exit(1);
|
| 175 | }
|
| 176 | }
|
| 177 | @RolesAllowed({"org.onap.aaf.myapp.myPerm|myInstance|myAction"})
|
| 178 | public static class MyServlet implements Servlet {
|
| 179 | private ServletConfig servletConfig;
|
| 180 |
|
| 181 | public void init(ServletConfig config) throws ServletException {
|
| 182 | servletConfig = config;
|
| 183 | }
|
| 184 |
|
| 185 | public ServletConfig getServletConfig() {
|
| 186 | return servletConfig;
|
| 187 | }
|
| 188 |
|
| 189 | public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
| 190 | HttpServletRequest request;
|
| 191 | try {
|
| 192 | request = (HttpServletRequest)req;
|
| 193 | } catch (ClassCastException e) {
|
| 194 | throw new ServletException("Only serving HTTP today",e);
|
| 195 | }
|
| 196 |
|
| 197 | res.getOutputStream().print("<html><header><title>CSP Servlet Test</title></header><body><h1>You're good to go!</h1><pre>" +
|
| 198 | request.getUserPrincipal());
|
| 199 |
|
| 200 | String perm = request.getParameter("PERM");
|
| 201 | if(perm!=null)
|
| 202 | if(request.isUserInRole(perm)) {
|
| 203 | if(perm.indexOf('|')<0)
|
| 204 | res.getOutputStream().print("\nCongrats!, You are in Role " + perm);
|
| 205 | else
|
| 206 | res.getOutputStream().print("\nCongrats!, You have Permission " + perm);
|
| 207 | } else {
|
| 208 | if(perm.indexOf('|')<0)
|
| 209 | res.getOutputStream().print("\nSorry, you are NOT in Role " + perm);
|
| 210 | else
|
| 211 | res.getOutputStream().print("\nSorry, you do NOT have Permission " + perm);
|
| 212 | }
|
| 213 |
|
| 214 | res.getOutputStream().print("</pre></body></html>");
|
| 215 |
|
| 216 | }
|
| 217 |
|
| 218 | public String getServletInfo() {
|
| 219 | return "MyServlet";
|
| 220 | }
|
| 221 |
|
| 222 | public void destroy() {
|
| 223 | }
|
| 224 | }
|
| 225 | }
|
| 226 |
|
| 227 | Java Direct (AAFLur) Method
|
| 228 | ===========================
|
| 229 | The AAFLur is the exact component used within all the Plugins mentioned above. It is written so that it can be called standalone as well, see the Example as follows
|
| 230 | package org.onap.aaf.example;
|
| 231 |
|
| 232 | import java.util.ArrayList;
|
| 233 | import java.util.List;
|
| 234 | import java.util.Properties;
|
| 235 |
|
| 236 | import org.onap.aaf.cadi.Access;
|
| 237 | import org.onap.aaf.cadi.Permission;
|
| 238 | import org.onap.aaf.cadi.aaf.v2_0.AAFAuthn;
|
| 239 | import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
|
| 240 | import org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm;
|
| 241 | import org.onap.aaf.cadi.config.Config;
|
| 242 | import org.onap.aaf.cadi.lur.aaf.AAFPermission;
|
| 243 | import org.onap.aaf.cadi.lur.aaf.test.TestAccess;
|
| 244 |
|
| 245 | public class ExamplePerm2_0 {
|
| 246 | public static void main(String args[]) {
|
| 247 | // Normally, these should be set in environment. Setting here for clarity
|
| 248 | Properties props = System.getProperties();
|
| 249 | props.setProperty("AFT_LATITUDE", "32.780140");
|
| 250 | props.setProperty("AFT_LONGITUDE", "-96.800451");
|
| 251 | props.setProperty("AFT_ENVIRONMENT", "AFTUAT");
|
| 252 | props.setProperty(Config.AAF_URL,
|
| 253 | "https://DME2RESOLVE/service=org.onap.aaf.authz.AuthorizationService/version=2.0/envContext=TEST/routeOffer=BAU_SE"
|
| 254 | );
|
| 255 | props.setProperty(Config.AAF_USER_EXPIRES,Integer.toString(5*60000)); // 5 minutes for found items to live in cache
|
| 256 | props.setProperty(Config.AAF_HIGH_COUNT,Integer.toString(400)); // Maximum number of items in Cache);
|
| 257 | props.setProperty(Config.CADI_KEYFILE,"keyfile"); //Note: Be sure to generate with java -jar <cadi_path>/lib/cadi-core*.jar keygen keyfile
|
| 258 | // props.setProperty("DME2_EP_REGISTRY_CLASS","DME2FS");
|
| 259 | // props.setProperty("AFT_DME2_EP_REGISTRY_FS_DIR","../../authz/dme2reg");
|
| 260 |
|
| 261 |
|
| 262 | // Link or reuse to your Logging mechanism
|
| 263 | Access myAccess = new TestAccess(); //
|
| 264 |
|
| 265 | //
|
| 266 | try {
|
| 267 | AAFCon<?> con = new AAFConDME2(myAccess);
|
| 268 |
|
| 269 | // AAFLur has pool of DME clients as needed, and Caches Client lookups
|
| 270 | AAFLurPerm aafLur = con.newLur();
|
| 271 | // Note: If you need both Authn and Authz construct the following:
|
| 272 | AAFAuthn<?> aafAuthn = con.newAuthn(aafLur);
|
| 273 |
|
| 274 | // Do not set Mech ID until after you construct AAFAuthn,
|
| 275 | // because we initiate "401" info to determine the Realm of
|
| 276 | // of the service we're after.
|
| 277 | con.basicAuth("xxxx@aaf.abc.com", "XXXXXX");
|
| 278 |
|
| 279 | try {
|
| 280 |
|
| 281 | // Normally, you obtain Principal from Authentication System.
|
| 282 | // For J2EE, you can ask the HttpServletRequest for getUserPrincipal()
|
| 283 | // If you use CADI as Authenticator, it will get you these Principals from
|
| 284 | // CSP or BasicAuth mechanisms.
|
| 285 | String id = "xxxx@aaf.abc.com"; //"cluster_admin@gridcore.abc.com";
|
| 286 |
|
| 287 | // If Validate succeeds, you will get a Null, otherwise, you will a String for the reason.
|
| 288 | String ok = aafAuthn.validate(id, "XXXXXX");
|
| 289 | if(ok!=null)System.out.println(ok);
|
| 290 |
|
| 291 | ok = aafAuthn.validate(id, "wrongPass");
|
| 292 | if(ok!=null)System.out.println(ok);
|
| 293 |
|
| 294 |
|
| 295 | // AAF Style permissions are in the form
|
| 296 | // Type, Instance, Action
|
| 297 | AAFPermission perm = new AAFPermission("org.onap.aaf.grid.core.coh",":dev_cluster", "WRITE");
|
| 298 |
|
| 299 | // Now you can ask the LUR (Local Representative of the User Repository about Authorization
|
| 300 | // With CADI, in J2EE, you can call isUserInRole("org.onap.aaf.mygroup|mytype|write") on the Request Object
|
| 301 | // instead of creating your own LUR
|
| 302 | System.out.println("Does " + id + " have " + perm);
|
| 303 | if(aafLur.fish(id, perm)) {
|
| 304 | System.out.println("Yes, you have permission");
|
| 305 | } else {
|
| 306 | System.out.println("No, you don't have permission");
|
| 307 | }
|
| 308 |
|
| 309 | System.out.println("Does Bogus have " + perm);
|
| 310 | if(aafLur.fish("Bogus", perm)) {
|
| 311 | System.out.println("Yes, you have permission");
|
| 312 | } else {
|
| 313 | System.out.println("No, you don't have permission");
|
| 314 | }
|
| 315 |
|
| 316 | // Or you can all for all the Permissions available
|
| 317 | List<Permission> perms = new ArrayList<Permission>();
|
| 318 |
|
| 319 | aafLur.fishAll(id,perms);
|
| 320 | for(Permission prm : perms) {
|
| 321 | System.out.println(prm.getKey());
|
| 322 | }
|
| 323 |
|
| 324 | // It might be helpful in some cases to clear the User's identity from the Cache
|
| 325 | aafLur.remove(id);
|
| 326 | } finally {
|
| 327 | aafLur.destroy();
|
| 328 | }
|
| 329 | } catch (Exception e) {
|
| 330 | e.printStackTrace();
|
| 331 | }
|
| 332 |
|
| 333 | }
|
| 334 | }
|
| 335 |
|
| 336 | There are two current AAF Lurs which you can utilize:
|
| 337 | Org.onap.aaf.cadi.aaf.v2_0.AAFLurPerm is the default, and will fish based on the Three-fold "Permission" standard in AAF
|
| 338 | To run this code, you will need from a SWM deployment (org.onap.aaf.cadi:cadi, then soft link to jars needed):
|
| 339 | cadi-core-<version>.jar
|
| 340 | cadi-aaf-<version>-full.jar
|
| 341 | or by Maven
|
| 342 | <dependency>
|
| 343 | <groupId>org.onap.aaf.cadi</groupId>
|
| 344 | <artifactId>cadi-aaf</artifactId>
|
| 345 | <version>THE_LATEST_VERSION</version>
|
| 346 | <classifier>full</classifier>
|
| 347 | </dependency>
|
| 348 | If you need the Java Client definitions only,
|
| 349 |
|
| 350 | Also needed are the DME2 Client libraries:
|
| 351 | dme2-<version>.jar
|
| 352 | discovery-clt-<version>.jar
|
| 353 |
|