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