2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6 * Modifications Copyright (C) 2019 IBM.
7 * ===========================================================================
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ============LICENSE_END====================================================
23 package org.onap.aaf.auth.cm.service;
25 import java.io.IOException;
26 import java.net.InetAddress;
27 import java.net.UnknownHostException;
28 import java.nio.ByteBuffer;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.cert.Certificate;
31 import java.security.cert.X509Certificate;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Date;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.List;
40 import org.onap.aaf.auth.cm.AAF_CM;
41 import org.onap.aaf.auth.cm.ca.CA;
42 import org.onap.aaf.auth.cm.ca.X509andChain;
43 import org.onap.aaf.auth.cm.cert.BCFactory;
44 import org.onap.aaf.auth.cm.cert.CSRMeta;
45 import org.onap.aaf.auth.cm.data.CertDrop;
46 import org.onap.aaf.auth.cm.data.CertRenew;
47 import org.onap.aaf.auth.cm.data.CertReq;
48 import org.onap.aaf.auth.cm.data.CertResp;
49 import org.onap.aaf.auth.cm.validation.CertmanValidator;
50 import org.onap.aaf.auth.common.Define;
51 import org.onap.aaf.auth.dao.CassAccess;
52 import org.onap.aaf.auth.dao.cass.ArtiDAO;
53 import org.onap.aaf.auth.dao.cass.CacheInfoDAO;
54 import org.onap.aaf.auth.dao.cass.CertDAO;
55 import org.onap.aaf.auth.dao.cass.CertDAO.Data;
56 import org.onap.aaf.auth.dao.cass.CredDAO;
57 import org.onap.aaf.auth.dao.cass.HistoryDAO;
58 import org.onap.aaf.auth.dao.cass.Status;
59 import org.onap.aaf.auth.dao.hl.Question;
60 import org.onap.aaf.auth.env.AuthzTrans;
61 import org.onap.aaf.auth.layer.Result;
62 import org.onap.aaf.auth.org.Organization;
63 import org.onap.aaf.auth.org.Organization.Identity;
64 import org.onap.aaf.auth.org.OrganizationException;
65 import org.onap.aaf.cadi.Access.Level;
66 import org.onap.aaf.cadi.Hash;
67 import org.onap.aaf.cadi.Permission;
68 import org.onap.aaf.cadi.aaf.AAFPermission;
69 import org.onap.aaf.cadi.config.Config;
70 import org.onap.aaf.cadi.configure.Factory;
71 import org.onap.aaf.cadi.util.FQI;
72 import org.onap.aaf.misc.env.APIException;
73 import org.onap.aaf.misc.env.util.Chrono;
75 public class CMService {
76 // If we add more CAs, may want to parameterize
77 private static final int STD_RENEWAL = 30;
78 private static final int MAX_RENEWAL = 60;
79 private static final int MIN_RENEWAL = 10;
80 // Limit total requests
81 private static final int MAX_X509s = 200; // Need a "LIMIT Exception" DB.
82 private static final String MAX_X509S_TAG = "cm_max_x509s"; // be able to adjust limit in future
84 public static final String REQUEST = "request";
85 public static final String IGNORE_IPS = "ignoreIPs";
86 public static final String RENEW = "renew";
87 public static final String DROP = "drop";
88 public static final String DOMAIN = "domain";
89 public static final String DYNAMIC_SANS="dynamic_sans";
91 private static final String CERTMAN = "certman";
92 private static final String ACCESS = "access";
94 private static final String[] NO_NOTES = new String[0];
95 private final Permission root_read_permission;
96 private final String aaf_ns;
98 private final CertDAO certDAO;
99 private final CredDAO credDAO;
100 private final ArtiDAO artiDAO;
101 private AAF_CM certManager;
102 private Boolean allowIgnoreIPs;
103 private AAFPermission limitOverridePerm;
104 private int max_509s;
106 // @SuppressWarnings("unchecked")
107 public CMService(final AuthzTrans trans, AAF_CM certman) throws APIException, IOException {
108 // Jonathan 4/2015 SessionFilter unneeded... DataStax already deals with
109 // Multithreading well
111 HistoryDAO hd = new HistoryDAO(trans, certman.cluster, CassAccess.KEYSPACE);
112 CacheInfoDAO cid = new CacheInfoDAO(trans, hd);
113 certDAO = new CertDAO(trans, hd, cid);
114 credDAO = new CredDAO(trans, hd, cid);
115 artiDAO = new ArtiDAO(trans, hd, cid);
117 this.certManager = certman;
119 aaf_ns = trans.getProperty(Config.AAF_ROOT_NS, Config.AAF_ROOT_NS_DEF);
120 root_read_permission=new AAFPermission(
127 max_509s = Integer.parseInt(trans.env().getProperty(MAX_X509S_TAG,Integer.toString(MAX_X509s)));
128 } catch (Exception e) {
129 trans.env().log(e, "");
130 max_509s = MAX_X509s;
132 limitOverridePerm = new AAFPermission(Define.ROOT_NS(),"certman","quantity","override");
133 allowIgnoreIPs = Boolean.valueOf(certman.access.getProperty(Config.CM_ALLOW_IGNORE_IPS, "false"));
135 trans.env().access().log(Level.INIT, "Allowing DNS Evaluation to be turned off with <ns>.certman|<ca name>|"+IGNORE_IPS);
139 public Result<CertResp> requestCert(final AuthzTrans trans, final Result<CertReq> req, final CA ca) {
141 if (req.value.fqdns.isEmpty()) {
142 return Result.err(Result.ERR_BadData, "No Machines passed in Request");
145 String key = req.value.fqdns.get(0);
147 // Policy 6: Requester must be granted Change permission in Namespace requested
148 String mechNS = FQI.reverseDomain(req.value.mechid);
149 if (mechNS == null) {
150 return Result.err(Status.ERR_Denied, "%s does not reflect a valid AAF Namespace", req.value.mechid);
153 List<String> notes = null;
155 boolean dynamic_sans = trans.fish(new AAFPermission(null, ca.getPermType(), ca.getName(),DYNAMIC_SANS));
156 boolean ignoreIPs = trans.fish(new AAFPermission(mechNS,CERTMAN, ca.getName(), IGNORE_IPS));
157 boolean domain_based = false;
159 // Note: Many Cert Impls require FQDN in "CN=" to be in the SANS as well. Therefore, the "fqdn" variable
160 // includes main ID plus ADDITIONAL SANS at all times.
161 if(req.value.fqdns.isEmpty()) {
162 fqdns = new ArrayList<>();
165 // Only Template or Dynamic permitted to pass in FQDNs
166 if (req.value.fqdns.get(0).startsWith("*")) { // Domain set
167 if (trans.fish(new AAFPermission(null,ca.getPermType(), ca.getName(), DOMAIN))) {
170 return Result.err(Result.ERR_Denied,
171 "Domain based Authorizations (" + req.value.fqdns.get(0) + ") requires Exception");
174 fqdns = new ArrayList<>(req.value.fqdns);
180 Organization org = trans.org();
181 InetAddress primary = null;
182 // Organize incoming information to get to appropriate Artifact
183 if (!fqdns.isEmpty()) { // Passed in FQDNS, validated above
184 // Accept domain wild cards, but turn into real machines
185 // Need *domain.com:real.machine.domain.com:san.machine.domain.com:...
186 if (domain_based) { // Domain set
187 // check for Permission in Add Artifact?
188 String domain = fqdns.get(0).substring(1); // starts with *, see above
190 if (fqdns.isEmpty()) {
191 return Result.err(Result.ERR_Denied,
192 "Requests using domain require machine declaration");
196 InetAddress ia = InetAddress.getByName(fqdns.get(0));
198 return Result.err(Result.ERR_Denied,
199 "Request not made from matching IP matching domain");
200 } else if (ia.getHostName().endsWith(domain)) {
206 // Passed in FQDNs, but not starting with *
208 for (String cn : req.value.fqdns) {
210 InetAddress[] ias = InetAddress.getAllByName(cn);
211 Set<String> potentialSanNames = new HashSet<>();
212 for (InetAddress ia1 : ias) {
213 InetAddress ia2 = InetAddress.getByAddress(ia1.getAddress());
214 String ip = trans.ip();
215 if (primary == null && ip.equals(ia1.getHostAddress())) {
217 } else if (!cn.equals(ia1.getHostName())
218 && !ia2.getHostName().equals(ia2.getHostAddress())) {
219 potentialSanNames.add(ia1.getHostName());
222 } catch (UnknownHostException e1) {
223 trans.debug().log(e1);
224 return Result.err(Result.ERR_BadData, "There is no DNS lookup for %s", cn);
233 host = req.value.fqdns.get(0);
234 } else if (primary == null) {
235 return Result.err(Result.ERR_Denied, "Request not made from matching IP (%s)", req.value.fqdns.get(0));
237 String thost = primary.getHostName();
238 host = thost==null?primary.getHostAddress():thost;
241 ArtiDAO.Data add = null;
242 Result<List<ArtiDAO.Data>> ra = artiDAO.read(trans, req.value.mechid, host);
243 if (ra.isOKhasData()) {
244 add = ra.value.get(0); // single key
245 if(dynamic_sans && (add.sans!=null && !add.sans.isEmpty())) { // do not allow both Dynamic and Artifact SANS
246 return Result.err(Result.ERR_Denied,"Authorization must not include SANS when doing Dynamic SANS (%s, %s)", req.value.mechid, key);
250 ra = artiDAO.read(trans, req.value.mechid, key);
251 if (ra.isOKhasData()) { // is the Template available?
252 add = ra.value.get(0);
254 for (String s : fqdns) {
255 if (!s.equals(add.machine)) {
256 add.sans(true).add(s);
259 Result<ArtiDAO.Data> rc = artiDAO.create(trans, add); // Create new Artifact from Template
261 return Result.err(rc);
264 return Result.err(Result.ERR_Denied,"No Authorization Template for %s, %s", req.value.mechid, key);
267 return Result.err(Result.ERR_Denied,"No Authorization found for %s, %s", req.value.mechid, key);
271 // Add Artifact listed FQDNs
273 if (add.sans != null) {
274 for (String s : add.sans) {
275 if (!fqdns.contains(s)) {
282 // Policy 2: If Config marked as Expired, do not create or renew
283 Date now = new Date();
284 if (add.expires != null && now.after(add.expires)) {
285 return Result.err(Result.ERR_Policy, "Configuration for %s %s is expired %s", add.mechid,
286 add.machine, Chrono.dateFmt.format(add.expires));
289 // Policy 3: MechID must be current
290 Identity muser = org.getIdentity(trans, add.mechid);
291 if (muser == null || !muser.isFound()) {
292 return Result.err(Result.ERR_Policy, "AppID '%s' must exist in %s",add.mechid,org.getName());
295 // Policy 4: Sponsor must be current
296 Identity ouser = muser.responsibleTo();
297 if (ouser == null || !ouser.isFound()) {
298 return Result.err(Result.ERR_Policy, "%s does not have a current sponsor at %s", add.mechid,
300 } else if (ouser.mayOwn() != null) {
301 return Result.err(Result.ERR_Policy, "%s reports that %s cannot be responsible for %s",
302 org.getName(), trans.user());
305 // Set Email from most current Sponsor
306 email = ouser.email();
308 // Policy 5: keep Artifact data current
309 if (!ouser.fullID().equals(add.sponsor)) {
310 add.sponsor = ouser.fullID();
311 artiDAO.update(trans, add);
314 // Policy 7: Caller must be the MechID or have specifically delegated
316 if (!(trans.user().equals(req.value.mechid)
317 || trans.fish(new AAFPermission(mechNS,CERTMAN, ca.getName(), REQUEST)))) {
318 return Result.err(Status.ERR_Denied, "%s must have access to modify x509 certs in NS %s",
319 trans.user(), mechNS);
322 // Policy 8: IP Addresses allowed in Certs only by Permission
323 if(!trans.fish(new AAFPermission(aaf_ns,CERTMAN, ca.getName(), "ip"))) {
324 for(String fqdn : fqdns) {
325 if(CA.IPV4_PATTERN.matcher(fqdn).matches() || CA.IPV6_PATTERN.matcher(fqdn).matches()) {
326 return Result.err(Status.ERR_Denied,
327 "Machines include a IP Address. IP Addresses are not allowed except by Permission");
332 // Make sure Primary is the first in fqdns
334 if (fqdns.size() > 1) {
335 for (int i = 0; i < fqdns.size(); ++i) {
336 if (primary==null && !ignoreIPs) {
337 trans.error().log("CMService var primary is null");
339 String fg = fqdns.get(i);
340 if ((fg!=null && primary!=null && fg.equals(primary.getHostName()))&&(i != 0)) {
341 String tmp = fqdns.get(0);
342 fqdns.set(0, primary.getHostName());
348 } catch (Exception e) {
349 trans.error().log(e);
350 return Result.err(Status.ERR_Denied,
351 "AppID Sponsorship cannot be determined at this time. Try later.");
356 csrMeta = BCFactory.createCSRMeta(ca, req.value.mechid, email, fqdns);
357 csrMeta.environment(ca.getEnv());
359 // Before creating, make sure they don't have too many
360 if(!trans.fish(limitOverridePerm)) {
361 Result<List<CertDAO.Data>> existing = certDAO.readID(trans, req.value.mechid);
362 if(existing.isOK()) {
363 String cn = "CN=" + csrMeta.cn();
365 Date now = new Date();
366 for (CertDAO.Data cdd : existing.value) {
367 Collection<? extends Certificate> certs = Factory.toX509Certificate(cdd.x509);
368 for(Iterator<? extends Certificate> iter = certs.iterator(); iter.hasNext();) {
369 X509Certificate x509 = (X509Certificate)iter.next();
370 if((x509.getNotAfter().after(now) && x509.getSubjectDN().getName().contains(cn))&&(++count>max_509s)) {
376 return Result.err(Result.ERR_Denied, "There are too many Certificates generated for " + cn + " for " + req.value.mechid);
380 // Here is where we send off to CA for Signing.
381 X509andChain x509ac = ca.sign(trans, csrMeta);
382 if (x509ac == null) {
383 return Result.err(Result.ERR_ActionNotCompleted, "x509 Certificate not signed by CA");
385 trans.info().printf("X509 Subject: %s", x509ac.getX509().getSubjectDN());
387 X509Certificate x509 = x509ac.getX509();
388 CertDAO.Data cdd = new CertDAO.Data();
389 cdd.ca = ca.getName();
390 cdd.serial = x509.getSerialNumber();
391 cdd.id = req.value.mechid;
392 cdd.x500 = x509.getSubjectDN().getName();
393 cdd.x509 = Factory.toString(trans, x509);
395 certDAO.create(trans, cdd);
397 CredDAO.Data crdd = new CredDAO.Data();
398 crdd.other = Question.random.nextInt();
399 crdd.cred = getChallenge256SaltedHash(csrMeta.challenge(), crdd.other);
400 crdd.expires = x509.getNotAfter();
401 crdd.id = req.value.mechid;
402 crdd.ns = Question.domain2ns(crdd.id);
403 crdd.type = CredDAO.CERT_SHA256_RSA;
404 crdd.tag = cdd.ca + '|' + cdd.serial.toString();
405 credDAO.create(trans, crdd);
407 CertResp cr = new CertResp(trans, ca, x509, csrMeta, x509ac.getTrustChain(), compileNotes(notes));
408 return Result.ok(cr);
409 } catch (Exception e) {
410 trans.debug().log(e);
411 return Result.err(Result.ERR_ActionNotCompleted, e.getMessage());
414 return Result.err(req);
418 public Result<CertResp> renewCert(AuthzTrans trans, Result<CertRenew> renew) {
420 return Result.err(Result.ERR_NotImplemented, "Not implemented yet");
422 return Result.err(renew);
426 public Result<Void> dropCert(AuthzTrans trans, Result<CertDrop> drop) {
428 return Result.err(Result.ERR_NotImplemented, "Not implemented yet");
430 return Result.err(drop);
434 public Result<List<Data>> readCertsByMechID(AuthzTrans trans, String mechID) {
435 // Policy 1: To Read, must have NS Read or is Sponsor
436 String ns = Question.domain2ns(mechID);
438 if (trans.user().equals(mechID) || trans.fish(new AAFPermission(ns,ACCESS, "*", "read"))
439 || (trans.org().validate(trans, Organization.Policy.OWNS_MECHID, null, mechID)) == null) {
440 return certDAO.readID(trans, mechID);
442 return Result.err(Result.ERR_Denied, "%s is not the ID, Sponsor or NS Owner/Admin for %s at %s",
443 trans.user(), mechID, trans.org().getName());
445 } catch (OrganizationException e) {
446 return Result.err(e);
450 public Result<CertResp> requestPersonalCert(AuthzTrans trans, CA ca) {
451 if (ca.inPersonalDomains(trans.getUserPrincipal())) {
452 Organization org = trans.org();
454 // Policy 1: MechID must be current
457 ouser = org.getIdentity(trans, trans.user());
458 } catch (OrganizationException e1) {
459 trans.debug().log(e1);
463 return Result.err(Result.ERR_Policy, "Requesting User must exist in %s", org.getName());
466 // Set Email from most current Sponsor
470 csrMeta = BCFactory.createPersonalCSRMeta(ca, trans.user(), ouser.email());
471 X509andChain x509ac = ca.sign(trans, csrMeta);
472 if (x509ac == null) {
473 return Result.err(Result.ERR_ActionNotCompleted, "x509 Certificate not signed by CA");
475 X509Certificate x509 = x509ac.getX509();
476 CertDAO.Data cdd = new CertDAO.Data();
477 cdd.ca = ca.getName();
478 cdd.serial = x509.getSerialNumber();
479 cdd.id = trans.user();
480 cdd.x500 = x509.getSubjectDN().getName();
481 cdd.x509 = Factory.toString(trans, x509);
482 certDAO.create(trans, cdd);
484 CertResp cr = new CertResp(trans, ca, x509, csrMeta, x509ac.getTrustChain(), compileNotes(null));
485 return Result.ok(cr);
486 } catch (Exception e) {
487 trans.debug().log(e);
488 return Result.err(Result.ERR_ActionNotCompleted, e.getMessage());
491 return Result.err(Result.ERR_Denied, trans.user(), " not supported for CA", ca.getName());
498 public Result<Void> createArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {
499 CertmanValidator v = new CertmanValidator().artisRequired(list, 1);
501 return Result.err(Result.ERR_BadData, v.errs());
503 for (ArtiDAO.Data add : list) {
505 // Policy 1: MechID must exist in Org
506 Identity muser = trans.org().getIdentity(trans, add.mechid);
508 return Result.err(Result.ERR_Denied, "%s is not valid for %s", add.mechid, trans.org().getName());
511 // Policy 2: MechID must have valid Organization Owner
513 if (muser.isPerson()) {
516 Identity ouser = muser.responsibleTo();
518 return Result.err(Result.ERR_Denied, "%s is not a valid Sponsor for %s at %s", trans.user(),
519 add.mechid, trans.org().getName());
522 // Policy 3: Calling ID must be MechID Owner
523 if (!trans.user().startsWith(ouser.id())) {
524 return Result.err(Result.ERR_Denied, "%s is not the Sponsor for %s at %s", trans.user(),
525 add.mechid, trans.org().getName());
530 // Policy 4: Renewal Days are between 10 and 60 (constants, may be
532 if (add.renewDays < MIN_RENEWAL) {
533 add.renewDays = STD_RENEWAL;
534 } else if (add.renewDays > MAX_RENEWAL) {
535 add.renewDays = MAX_RENEWAL;
538 // Policy 5: If Notify is blank, set to Owner's Email
539 if (add.notify == null || add.notify.length() == 0) {
540 add.notify = "mailto:" + emailUser.email();
543 // Policy 6: Only do Domain by Exception
544 if (add.machine.startsWith("*")) { // Domain set
545 CA ca = certManager.getCA(add.ca);
546 if (!trans.fish(new AAFPermission(ca.getPermNS(),ca.getPermType(), add.ca, DOMAIN))) {
547 return Result.err(Result.ERR_Denied, "Domain Artifacts (%s) requires specific Permission",
552 // Set Sponsor from Golden Source
553 add.sponsor = emailUser.fullID();
555 } catch (OrganizationException e) {
556 return Result.err(e);
559 Result<ArtiDAO.Data> rv = artiDAO.create(trans, add);
560 // come up with Partial Reporting Scheme, or allow only one at a time.
562 return Result.err(rv);
568 public Result<List<ArtiDAO.Data>> readArtifacts(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {
569 CertmanValidator v = new CertmanValidator().keys(add);
571 return Result.err(Result.ERR_BadData, v.errs());
573 Result<List<ArtiDAO.Data>> data = artiDAO.read(trans, add);
574 if (data.notOKorIsEmpty()) {
577 add = data.value.get(0);
578 if (trans.user().equals(add.mechid)
579 || trans.fish(root_read_permission,
580 new AAFPermission(add.ns,ACCESS, "*", "read"),
581 new AAFPermission(add.ns,CERTMAN, add.ca, "read"),
582 new AAFPermission(add.ns,CERTMAN, add.ca, REQUEST))
583 || (trans.org().validate(trans, Organization.Policy.OWNS_MECHID, null, add.mechid)) == null) {
586 return Result.err(Result.ERR_Denied,
587 "%s is not %s, is not the sponsor, and doesn't have delegated permission.", trans.user(),
588 add.mechid, add.ns + ".certman|" + add.ca + "|read or ...|request"); // note: reason is set by 2nd
589 // case, if 1st case misses
594 public Result<List<ArtiDAO.Data>> readArtifactsByMechID(AuthzTrans trans, String mechid)
595 throws OrganizationException {
596 CertmanValidator v = new CertmanValidator();
597 v.nullOrBlank("mechid", mechid);
599 return Result.err(Result.ERR_BadData, v.errs());
601 String ns = FQI.reverseDomain(mechid);
604 if (trans.fish(new AAFPermission(ns, ACCESS, "*", "read"))
605 || (reason = trans.org().validate(trans, Organization.Policy.OWNS_MECHID, null, mechid)) == null) {
606 return artiDAO.readByMechID(trans, mechid);
608 return Result.err(Result.ERR_Denied, reason); // note: reason is set by 2nd case, if 1st case misses
613 public Result<List<ArtiDAO.Data>> readArtifactsByMachine(AuthzTrans trans, String machine) {
614 CertmanValidator v = new CertmanValidator();
615 v.nullOrBlank("machine", machine);
617 return Result.err(Result.ERR_BadData, v.errs());
622 return artiDAO.readByMachine(trans, machine);
625 public Result<List<ArtiDAO.Data>> readArtifactsByNs(AuthzTrans trans, String ns) {
626 CertmanValidator v = new CertmanValidator();
627 v.nullOrBlank("ns", ns);
629 return Result.err(Result.ERR_BadData, v.errs());
633 return artiDAO.readByNs(trans, ns);
636 public Result<Void> updateArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) throws OrganizationException {
637 CertmanValidator v = new CertmanValidator();
638 v.artisRequired(list, 1);
640 return Result.err(Result.ERR_BadData, v.errs());
643 // Check if requesting User is Sponsor
644 // Shall we do one, or multiples?
645 for (ArtiDAO.Data add : list) {
646 // Policy 1: MechID must exist in Org
647 Identity muser = trans.org().getIdentity(trans, add.mechid);
649 return Result.err(Result.ERR_Denied, "%s is not valid for %s", add.mechid, trans.org().getName());
652 // Policy 2: MechID must have valid Organization Owner
653 Identity ouser = muser.responsibleTo();
655 return Result.err(Result.ERR_Denied, "%s is not a valid Sponsor for %s at %s", trans.user(), add.mechid,
656 trans.org().getName());
659 // Policy 3: Renewal Days are between 10 and 60 (constants, may be
661 if (add.renewDays < MIN_RENEWAL) {
662 add.renewDays = STD_RENEWAL;
663 } else if (add.renewDays > MAX_RENEWAL) {
664 add.renewDays = MAX_RENEWAL;
667 // Policy 4: Data is always updated with the latest Sponsor
668 // Add to Sponsor, to make sure we are always up to date.
669 add.sponsor = ouser.fullID();
671 // Policy 5: If Notify is blank, set to Owner's Email
672 if (add.notify == null || add.notify.length() == 0) {
673 add.notify = "mailto:" + ouser.email();
675 // Policy 6: Only do Domain by Exception
676 if (add.machine.startsWith("*")) { // Domain set
677 CA ca = certManager.getCA(add.ca);
679 return Result.err(Result.ERR_BadData, "CA is required in Artifact");
681 if (!trans.fish(new AAFPermission(null,ca.getPermType(), add.ca, DOMAIN))) {
682 return Result.err(Result.ERR_Denied, "Domain Artifacts (%s) requires specific Permission",
687 // Policy 7: only Owner may update info
688 if (trans.user().startsWith(ouser.id())) {
689 return artiDAO.update(trans, add);
691 return Result.err(Result.ERR_Denied, "%s may not update info for %s", trans.user(), muser.fullID());
694 return Result.err(Result.ERR_BadData, "No Artifacts to update");
697 public Result<Void> deleteArtifact(AuthzTrans trans, String mechid, String machine) throws OrganizationException {
698 CertmanValidator v = new CertmanValidator();
699 v.nullOrBlank("mechid", mechid).nullOrBlank("machine", machine);
701 return Result.err(Result.ERR_BadData, v.errs());
704 Result<List<ArtiDAO.Data>> rlad = artiDAO.read(trans, mechid, machine);
705 if (rlad.notOKorIsEmpty()) {
706 return Result.err(Result.ERR_NotFound, "Artifact for %s %s does not exist.", mechid, machine);
709 return deleteArtifact(trans, rlad.value.get(0));
712 private Result<Void> deleteArtifact(AuthzTrans trans, ArtiDAO.Data add) throws OrganizationException {
713 // Policy 1: Record should be delete able only by Existing Sponsor.
714 String sponsor = null;
715 Identity muser = trans.org().getIdentity(trans, add.mechid);
717 Identity ouser = muser.responsibleTo();
719 sponsor = ouser.fullID();
722 // Policy 1.a: If Sponsorship is deleted in system of Record, then
723 // accept deletion by sponsor in Artifact Table
724 if (sponsor == null) {
725 sponsor = add.sponsor;
728 String ns = FQI.reverseDomain(add.mechid);
730 if (trans.fish(new AAFPermission(ns,ACCESS, "*", "write")) || trans.user().equals(sponsor)) {
731 return artiDAO.delete(trans, add, false);
733 return Result.err(Result.ERR_Denied, "%1 is not allowed to delete this item", trans.user());
736 public Result<Void> deleteArtifact(AuthzTrans trans, List<ArtiDAO.Data> list) {
737 CertmanValidator v = new CertmanValidator().artisRequired(list, 1);
739 return Result.err(Result.ERR_BadData, v.errs());
743 boolean partial = false;
744 Result<Void> result = null;
745 for (ArtiDAO.Data add : list) {
746 result = deleteArtifact(trans, add);
747 if (result.notOK()) {
751 if (result == null) {
752 result = Result.err(Result.ERR_BadData, "No Artifacts to delete");
753 } else if (partial) {
754 result.partialContent(true);
757 } catch (Exception e) {
758 return Result.err(e);
762 private String[] compileNotes(List<String> notes) {
767 rv = new String[notes.size()];
773 private ByteBuffer getChallenge256SaltedHash(String challenge, int salt) throws NoSuchAlgorithmException {
774 ByteBuffer bb = ByteBuffer.allocate(Integer.SIZE + challenge.length());
776 bb.put(challenge.getBytes());
777 byte[] hash = Hash.hashSHA256(bb.array());
778 return ByteBuffer.wrap(hash);