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.cm.facade;
24 import static org.onap.aaf.auth.layer.Result.ERR_ActionNotCompleted;
25 import static org.onap.aaf.auth.layer.Result.ERR_BadData;
26 import static org.onap.aaf.auth.layer.Result.ERR_ConflictAlreadyExists;
27 import static org.onap.aaf.auth.layer.Result.ERR_Denied;
28 import static org.onap.aaf.auth.layer.Result.ERR_NotFound;
29 import static org.onap.aaf.auth.layer.Result.ERR_NotImplemented;
30 import static org.onap.aaf.auth.layer.Result.ERR_Policy;
31 import static org.onap.aaf.auth.layer.Result.ERR_Security;
32 import static org.onap.aaf.auth.layer.Result.OK;
34 import java.io.IOException;
35 import java.security.KeyStore;
36 import java.security.KeyStoreException;
37 import java.security.NoSuchAlgorithmException;
38 import java.security.PrivateKey;
39 import java.security.cert.Certificate;
40 import java.security.cert.CertificateException;
41 import java.security.cert.X509Certificate;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.List;
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletResponse;
49 import org.onap.aaf.auth.cm.AAF_CM;
50 import org.onap.aaf.auth.cm.ca.CA;
51 import org.onap.aaf.auth.cm.data.CertResp;
52 import org.onap.aaf.auth.cm.mapper.Mapper;
53 import org.onap.aaf.auth.cm.mapper.Mapper.API;
54 import org.onap.aaf.auth.cm.service.CMService;
55 import org.onap.aaf.auth.dao.cass.ArtiDAO;
56 import org.onap.aaf.auth.dao.cass.Status;
57 import org.onap.aaf.auth.env.AuthzEnv;
58 import org.onap.aaf.auth.env.AuthzTrans;
59 import org.onap.aaf.auth.layer.Result;
60 import org.onap.aaf.cadi.aaf.AAFPermission;
61 import org.onap.aaf.cadi.configure.CertException;
62 import org.onap.aaf.cadi.configure.Factory;
63 import org.onap.aaf.misc.env.APIException;
64 import org.onap.aaf.misc.env.Data;
65 import org.onap.aaf.misc.env.Env;
66 import org.onap.aaf.misc.env.TimeTaken;
67 import org.onap.aaf.misc.env.util.Split;
68 import org.onap.aaf.misc.rosetta.env.RosettaDF;
69 import org.onap.aaf.misc.rosetta.env.RosettaData;
74 * This Service Facade encapsulates the essence of the API Service can do, and provides
75 * a single created object for elements such as RosettaDF.
77 * The Responsibilities of this class are to:
78 * 1) Interact with the Service Implementation (which might be supported by various kinds of Backend Storage)
79 * 2) Validate incoming data (if applicable)
80 * 3) Convert the Service response into the right Format, and mark the Content Type
81 * a) In the future, we may support multiple Response Formats, aka JSON or XML, based on User Request.
82 * 4) Log Service info, warnings and exceptions as necessary
83 * 5) When asked by the API layer, this will create and write Error content to the OutputStream
85 * Note: This Class does NOT set the HTTP Status Code. That is up to the API layer, so that it can be
86 * clearly coordinated with the API Documentation
91 public abstract class FacadeImpl<REQ,CERT,ARTIFACTS,ERROR> extends org.onap.aaf.auth.layer.FacadeImpl implements Facade<REQ,CERT,ARTIFACTS,ERROR>
93 private static final String TRUE = "TRUE";
94 private static final String REQUEST_CERT = "Request New Certificate";
95 private static final String RENEW_CERT = "Renew Certificate";
96 private static final String DROP_CERT = "Drop Certificate";
97 private static final String READ_CERTS_MECHID = "Read Certificates by MechID";
98 private static final String CREATE_ARTIFACTS = "Create Deployment Artifact";
99 private static final String READ_ARTIFACTS = "Read Deployment Artifact";
100 private static final String UPDATE_ARTIFACTS = "Update Deployment Artifact";
101 private static final String DELETE_ARTIFACTS = "Delete Deployment Artifact";
103 private CMService service;
105 private final RosettaDF<ERROR> errDF;
106 private final RosettaDF<REQ> certRequestDF, certRenewDF, certDropDF;
107 private final RosettaDF<CERT> certDF;
108 private final RosettaDF<ARTIFACTS> artiDF;
109 private Mapper<REQ, CERT, ARTIFACTS, ERROR> mapper;
110 // private Slot sCertAuth;
111 private AAF_CM certman;
112 private final String voidResp;
114 public FacadeImpl(AAF_CM certman,
116 Mapper<REQ,CERT,ARTIFACTS,ERROR> mapper,
117 Data.TYPE dataType) throws APIException {
118 this.service = service;
119 this.mapper = mapper;
120 this.certman = certman;
121 AuthzEnv env = certman.env;
122 //TODO: Gabe [JUnit] Static issue, talk to Jonathan
123 (errDF = env.newDataFactory(mapper.getClass(API.ERROR))).in(dataType).out(dataType);
124 (certRequestDF = env.newDataFactory(mapper.getClass(API.CERT_REQ))).in(dataType).out(dataType);
125 (certRenewDF = env.newDataFactory(mapper.getClass(API.CERT_RENEW))).in(dataType).out(dataType);
126 (certDropDF = env.newDataFactory(mapper.getClass(API.CERT_DROP))).in(dataType).out(dataType);
127 (certDF = env.newDataFactory(mapper.getClass(API.CERT))).in(dataType).out(dataType);
128 (artiDF = env.newDataFactory(mapper.getClass(API.ARTIFACTS))).in(dataType).out(dataType);
129 // sCertAuth = env.slot(API_Cert.CERT_AUTH);
130 if(artiDF.getOutType().name().contains("xml")) {
131 voidResp = "application/Void+xml;charset=utf-8;version=1.0,application/xml;version=1.0,*/*";
133 voidResp = "application/Void+json;charset=utf-8;version=1.0,application/json;version=1.0,*/*";
137 public Mapper<REQ,CERT,ARTIFACTS,ERROR> mapper() {
142 * @see com.att.authz.facade.AuthzFacade#error(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, int)
144 * Note: Conforms to AT&T TSS RESTful Error Structure
147 public void error(AuthzTrans trans, HttpServletResponse response, Result<?> result) {
148 error(trans, response, result.status,
149 result.details==null?"":result.details.trim(),
150 result.variables==null?new String[0]:result.variables);
154 public void error(AuthzTrans trans, HttpServletResponse response, int status, final String _msg, final String ... _detail) {
157 boolean hidemsg=false;
160 case ERR_ActionNotCompleted:
162 prefix = "Accepted, Action not complete";
163 response.setStatus(/*httpstatus=*/202);
171 prefix = "Forbidden";
172 response.setStatus(/*httpstatus=*/403);
178 prefix = "Not Found";
179 response.setStatus(/*httpstatus=*/404);
185 prefix = "Not Acceptable";
186 response.setStatus(/*httpstatus=*/406);
190 case ERR_ConflictAlreadyExists:
192 prefix = "Conflict Already Exists";
193 response.setStatus(/*httpstatus=*/409);
197 case ERR_NotImplemented:
199 prefix = "Not Implemented";
200 response.setStatus(/*httpstatus=*/501);
206 prefix = "General Service Error";
207 response.setStatus(/*httpstatus=*/500);
213 StringBuilder holder = new StringBuilder();
214 ERROR em = mapper().errorFromMessage(holder, msgId,prefix + ": " + _msg,_detail);
223 em = mapper().errorFromMessage(holder, msgId, "Server had an issue processing this request");
225 errDF.newData(trans).load(em).to(response.getOutputStream());
227 } catch (Exception e) {
228 trans.error().log(e,"unable to send response for",_msg);
233 public Result<Void> check(AuthzTrans trans, HttpServletResponse resp, String perm) throws IOException {
234 String[] p = Split.split('|',perm);
236 return Result.err(Result.ERR_BadData,"Invalid Perm String");
238 AAFPermission ap = new AAFPermission(p[0],p[1],p[2]);
239 if(certman.aafLurPerm.fish(trans.getUserPrincipal(), ap)) {
240 resp.setContentType(voidResp);
241 resp.getOutputStream().write(0);
244 return Result.err(Result.ERR_Denied,"%s does not have %s",trans.user(),ap.getKey());
249 * @see com.att.auth.certman.facade.Facade#requestCert(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
252 public Result<Void> requestCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca) {
253 TimeTaken tt = trans.start(REQUEST_CERT, Env.SUB|Env.ALWAYS);
255 boolean withTrust=(wt=req.getParameter("withTrust"))!=null || TRUE.equalsIgnoreCase(wt);
259 Data<REQ> rd = certRequestDF.newData().load(req.getInputStream());
260 request = rd.asObject();
261 } catch(APIException e) {
262 trans.error().log("Invalid Input",IN,REQUEST_CERT);
263 return Result.err(Result.ERR_BadData,"Invalid Input");
266 Result<CertResp> rcr = service.requestCert(trans,mapper.toReq(trans,request), ca);
268 return Result.err(rcr);
271 // CA certAuth = trans.get(sCertAuth,null);
272 Result<CERT> rc = mapper.toCert(trans, rcr, withTrust);
275 RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
276 data.to(resp.getOutputStream());
278 setContentType(resp,certDF.getOutType());
281 return Result.err(rc);
284 } catch (Exception e) {
285 trans.error().log(e,IN,REQUEST_CERT);
286 return Result.err(e);
293 * @see org.onap.aaf.auth.cm.facade.Facade#requestPersonalCert(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, boolean)
296 public Result<Void> requestPersonalCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, CA ca) {
297 return Result.err(Result.ERR_NotImplemented,"not implemented yet");
298 // Result<CertResp> rcr = service.requestPersonalCert(trans,ca);
300 // return Result.err(rcr);
303 // resp.setContentType("application/zip, application/octet-stream");
304 // ZipOutputStream zos = new ZipOutputStream(resp.getOutputStream());
305 // PrintStream ps = new PrintStream(zos);
306 // ZipEntry ze = new ZipEntry(trans.user()+".key");
307 // zos.putNextEntry(ze);
308 // ps.print(rcr.value.privateString());
311 // zos.putNextEntry(new ZipEntry(trans.user()+".crt"));
312 // ps.print(rcr.value.asCertString());
316 // if((wt=req.getParameter("withTrust"))!=null || TRUE.equalsIgnoreCase(wt)) {
317 // zos.putNextEntry(new ZipEntry(trans.user()+".trustCrts"));
318 // for(String s : ca.getTrustChain()) {
324 // boolean withJKS = (wt=req.getParameter("withJKS"))!=null || TRUE.equalsIgnoreCase(wt);
326 // if(trans.getUserPrincipal() instanceof BasicPrincipal) {
327 // char[] cap = new String(((BasicPrincipal)trans.getUserPrincipal()).getCred()).toCharArray();
328 // KeyStore ks = keystore(trans, rcr.value, ca.getTrustChain(), trans.user(), cap);
329 // zos.putNextEntry(new ZipEntry(trans.user()+".jks"));
330 // ks.store(zos, cap);
335 // zos.putNextEntry(new ZipEntry("cert_deploy.sh"));
336 // ps.println("# Deploy Certificate to ~/.aaf");
337 // ps.println("if [ \"$1\" = \"\" ]; then echo \"sh deploy.sh <zipfile>\";exit; else chmod 700 $HOME/.aaf; fi");
338 // ps.println("chmod 600 $1");
339 // ps.println("if [ ! -e $HOME/.aaf ]; then mkdir -m 700 $HOME/.aaf; fi");
340 // ps.println("THE_PWD=`pwd`");
341 // ps.println("cd $HOME/.aaf");
342 // ps.println("echo \"Deploying to `pwd`\"");
343 // ps.println("jar -xvf $THE_PWD/$1 " + trans.user());
344 // ps.println("chmod 600 " + trans.user() + ".key");
346 // ps.println("chmod 600 " + trans.user() + ".jks");
348 // ps.println("cd $THE_PWD");
349 // ps.println("rm cert_deploy.sh");
355 // } catch (IOException | KeyStoreException | CertificateException | APIException | CertException | NoSuchAlgorithmException e) {
356 // return Result.err(e);
360 // return Result.ok();
363 private KeyStore keystore(AuthzTrans trans, CertResp cr, String[] trustChain, String name, char[] cap) throws KeyStoreException, CertificateException, APIException, IOException, CertException, NoSuchAlgorithmException {
364 KeyStore jks = KeyStore.getInstance("jks");
367 // Get the Cert(s)... Might include Trust store
368 List<String> lcerts = new ArrayList<>();
369 lcerts.add(cr.asCertString());
370 for(String s : trustChain) {
374 Collection<? extends Certificate> certColl = Factory.toX509Certificate(lcerts);
375 X509Certificate[] certs = new X509Certificate[certColl.size()];
376 certColl.toArray(certs);
377 KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(cap);
379 PrivateKey pk = Factory.toPrivateKey(trans, cr.privateString());
380 KeyStore.PrivateKeyEntry pkEntry =
381 new KeyStore.PrivateKeyEntry(pk, new Certificate[] {certs[0]});
382 jks.setEntry(name, pkEntry, protParam);
385 for(X509Certificate x509 : certs) {
386 jks.setCertificateEntry("cert_"+ ++i, x509);
392 public Result<Void> renewCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, boolean withTrust) {
393 TimeTaken tt = trans.start(RENEW_CERT, Env.SUB|Env.ALWAYS);
397 Data<REQ> rd = certRenewDF.newData().load(req.getInputStream());
398 request = rd.asObject();
399 } catch(APIException e) {
400 trans.error().log("Invalid Input",IN,RENEW_CERT);
401 return Result.err(Result.ERR_BadData,"Invalid Input");
404 // String certAuth = trans.get(sCertAuth,null);
405 Result<CertResp> rcr = service.renewCert(trans,mapper.toRenew(trans,request));
406 Result<CERT> rc = mapper.toCert(trans, rcr, withTrust);
410 RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
411 data.to(resp.getOutputStream());
413 setContentType(resp,certDF.getOutType());
416 return Result.err(rc);
418 } catch (Exception e) {
419 trans.error().log(e,IN,RENEW_CERT);
420 return Result.err(e);
428 public Result<Void> dropCert(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
429 TimeTaken tt = trans.start(DROP_CERT, Env.SUB|Env.ALWAYS);
433 Data<REQ> rd = certDropDF.newData().load(req.getInputStream());
434 request = rd.asObject();
435 } catch(APIException e) {
436 trans.error().log("Invalid Input",IN,DROP_CERT);
437 return Result.err(Result.ERR_BadData,"Invalid Input");
440 Result<Void> rv = service.dropCert(trans,mapper.toDrop(trans, request));
443 setContentType(resp,certRequestDF.getOutType());
446 return Result.err(rv);
448 } catch (Exception e) {
449 trans.error().log(e,IN,DROP_CERT);
450 return Result.err(e);
457 * @see org.onap.aaf.auth.cm.facade.Facade#readCertsByMechID(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, java.lang.String)
460 public Result<Void> readCertsByMechID(AuthzTrans trans, HttpServletResponse resp, String mechID) {
461 TimeTaken tt = trans.start(READ_CERTS_MECHID, Env.SUB|Env.ALWAYS);
463 Result<CERT> rc = mapper.toCert(trans, service.readCertsByMechID(trans,mechID));
466 RosettaData<CERT> data = certDF.newData(trans).load(rc.value);
467 data.to(resp.getOutputStream());
469 setContentType(resp,certDF.getOutType());
472 return Result.err(rc);
474 } catch (Exception e) {
475 trans.error().log(e,IN,READ_CERTS_MECHID);
476 return Result.err(e);
482 ////////////////////////////
484 ////////////////////////////
486 public Result<Void> createArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
487 TimeTaken tt = trans.start(CREATE_ARTIFACTS, Env.SUB);
491 Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
492 arti = rd.asObject();
493 } catch(APIException e) {
494 trans.error().log("Invalid Input",IN,CREATE_ARTIFACTS);
495 return Result.err(Result.ERR_BadData,"Invalid Input");
498 return service.createArtifact(trans,mapper.toArtifact(trans,arti));
499 } catch (Exception e) {
501 trans.error().log(e,IN,CREATE_ARTIFACTS);
502 return Result.err(e);
509 public Result<Void> readArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
510 TimeTaken tt = trans.start(READ_ARTIFACTS, Env.SUB);
512 String mechid = req.getParameter("mechid");
513 String machine = req.getParameter("machine");
514 String ns = req.getParameter("ns");
516 Result<ARTIFACTS> ra;
517 if( machine !=null && mechid == null) {
518 ra = mapper.fromArtifacts(service.readArtifactsByMachine(trans, machine));
519 } else if(mechid!=null && machine==null) {
520 ra = mapper.fromArtifacts(service.readArtifactsByMechID(trans, mechid));
521 } else if(mechid!=null && machine!=null) {
522 ArtiDAO.Data add = new ArtiDAO.Data();
524 add.machine = machine;
526 ra = mapper.fromArtifacts(service.readArtifacts(trans,add));
527 } else if(ns!=null) {
528 ra = mapper.fromArtifacts(service.readArtifactsByNs(trans, ns));
530 ra = Result.err(Status.ERR_BadData,"Invalid request inputs");
534 RosettaData<ARTIFACTS> data = artiDF.newData(trans).load(ra.value);
535 data.to(resp.getOutputStream());
536 setContentType(resp,artiDF.getOutType());
539 return Result.err(ra);
542 } catch (Exception e) {
543 trans.error().log(e,IN,READ_ARTIFACTS);
544 return Result.err(e);
551 public Result<Void> readArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine) {
552 TimeTaken tt = trans.start(READ_ARTIFACTS, Env.SUB);
554 ArtiDAO.Data add = new ArtiDAO.Data();
556 add.machine = machine;
557 Result<ARTIFACTS> ra = mapper.fromArtifacts(service.readArtifacts(trans,add));
559 RosettaData<ARTIFACTS> data = artiDF.newData(trans).load(ra.value);
560 data.to(resp.getOutputStream());
561 setContentType(resp,artiDF.getOutType());
564 return Result.err(ra);
566 } catch (Exception e) {
567 trans.error().log(e,IN,READ_ARTIFACTS);
568 return Result.err(e);
576 public Result<Void> updateArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
577 TimeTaken tt = trans.start(UPDATE_ARTIFACTS, Env.SUB);
581 Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
582 arti = rd.asObject();
583 } catch(APIException e) {
584 trans.error().log("Invalid Input",IN,UPDATE_ARTIFACTS);
585 return Result.err(Result.ERR_BadData,"Invalid Input");
588 return service.updateArtifact(trans,mapper.toArtifact(trans,arti));
589 } catch (Exception e) {
590 trans.error().log(e,IN,UPDATE_ARTIFACTS);
591 return Result.err(e);
598 public Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
599 TimeTaken tt = trans.start(DELETE_ARTIFACTS, Env.SUB);
603 Data<ARTIFACTS> rd = artiDF.newData().load(req.getInputStream());
604 arti = rd.asObject();
605 } catch(APIException e) {
606 trans.error().log("Invalid Input",IN,DELETE_ARTIFACTS);
607 return Result.err(Result.ERR_BadData,"Invalid Input");
610 Result<Void> rv = service.deleteArtifact(trans,mapper.toArtifact(trans,arti));
613 setContentType(resp,artiDF.getOutType());
616 } catch (Exception e) {
617 trans.error().log(e,IN,DELETE_ARTIFACTS);
618 return Result.err(e);
625 public Result<Void> deleteArtifacts(AuthzTrans trans, HttpServletResponse resp, String mechid, String machine) {
626 TimeTaken tt = trans.start(DELETE_ARTIFACTS, Env.SUB);
628 Result<Void> rv = service.deleteArtifact(trans, mechid, machine);
631 setContentType(resp,artiDF.getOutType());
634 } catch (Exception e) {
635 trans.error().log(e,IN,DELETE_ARTIFACTS);
636 return Result.err(e);