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.service;
24 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.force;
25 import static org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE.future;
26 import static org.onap.aaf.auth.layer.Result.OK;
27 import static org.onap.aaf.auth.rserv.HttpMethods.DELETE;
28 import static org.onap.aaf.auth.rserv.HttpMethods.GET;
29 import static org.onap.aaf.auth.rserv.HttpMethods.POST;
30 import static org.onap.aaf.auth.rserv.HttpMethods.PUT;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.Collections;
36 import java.util.Date;
37 import java.util.GregorianCalendar;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.List;
43 import java.util.TreeMap;
44 import java.util.UUID;
46 import javax.servlet.http.HttpServletRequest;
48 import org.onap.aaf.auth.common.Define;
49 import org.onap.aaf.auth.dao.DAOException;
50 import org.onap.aaf.auth.dao.cached.CachedPermDAO;
51 import org.onap.aaf.auth.dao.cached.CachedRoleDAO;
52 import org.onap.aaf.auth.dao.cached.CachedUserRoleDAO;
53 import org.onap.aaf.auth.dao.cass.ApprovalDAO;
54 import org.onap.aaf.auth.dao.cass.CertDAO;
55 import org.onap.aaf.auth.dao.cass.CredDAO;
56 import org.onap.aaf.auth.dao.cass.DelegateDAO;
57 import org.onap.aaf.auth.dao.cass.FutureDAO;
58 import org.onap.aaf.auth.dao.cass.HistoryDAO;
59 import org.onap.aaf.auth.dao.cass.Namespace;
60 import org.onap.aaf.auth.dao.cass.NsDAO;
61 import org.onap.aaf.auth.dao.cass.NsDAO.Data;
62 import org.onap.aaf.auth.dao.cass.NsSplit;
63 import org.onap.aaf.auth.dao.cass.NsType;
64 import org.onap.aaf.auth.dao.cass.PermDAO;
65 import org.onap.aaf.auth.dao.cass.RoleDAO;
66 import org.onap.aaf.auth.dao.cass.Status;
67 import org.onap.aaf.auth.dao.cass.UserRoleDAO;
68 import org.onap.aaf.auth.dao.hl.CassExecutor;
69 import org.onap.aaf.auth.dao.hl.Function;
70 import org.onap.aaf.auth.dao.hl.Function.FUTURE_OP;
71 import org.onap.aaf.auth.dao.hl.Function.Lookup;
72 import org.onap.aaf.auth.dao.hl.Function.OP_STATUS;
73 import org.onap.aaf.auth.dao.hl.PermLookup;
74 import org.onap.aaf.auth.dao.hl.Question;
75 import org.onap.aaf.auth.dao.hl.Question.Access;
76 import org.onap.aaf.auth.env.AuthzTrans;
77 import org.onap.aaf.auth.env.AuthzTrans.REQD_TYPE;
78 import org.onap.aaf.auth.layer.Result;
79 import org.onap.aaf.auth.org.Executor;
80 import org.onap.aaf.auth.org.Organization;
81 import org.onap.aaf.auth.org.Organization.Expiration;
82 import org.onap.aaf.auth.org.Organization.Identity;
83 import org.onap.aaf.auth.org.Organization.Policy;
84 import org.onap.aaf.auth.org.OrganizationException;
85 import org.onap.aaf.auth.rserv.doc.ApiDoc;
86 import org.onap.aaf.auth.service.mapper.Mapper;
87 import org.onap.aaf.auth.service.mapper.Mapper.API;
88 import org.onap.aaf.auth.service.validation.ServiceValidator;
89 import org.onap.aaf.auth.validation.Validator;
90 import org.onap.aaf.cadi.aaf.Defaults;
91 import org.onap.aaf.cadi.principal.BasicPrincipal;
92 import org.onap.aaf.cadi.util.FQI;
93 import org.onap.aaf.misc.env.Env;
94 import org.onap.aaf.misc.env.TimeTaken;
95 import org.onap.aaf.misc.env.util.Chrono;
96 import org.onap.aaf.misc.env.util.Split;
98 import aaf.v2_0.CredRequest;
101 * AuthzCassServiceImpl implements AuthzCassService for
116 public class AuthzCassServiceImpl <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS>
117 implements AuthzService <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> {
119 private static final String TWO_SPACE = " ";
120 private Mapper <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper;
122 public Mapper <NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper() {return mapper;}
124 private static final String ASTERIX = "*";
125 private static final String CACHE = "cache";
126 private static final String ROOT_NS = Define.ROOT_NS();
127 private static final String ROOT_COMPANY = Define.ROOT_COMPANY();
129 private final Question ques;
130 private final Function func;
132 public AuthzCassServiceImpl(AuthzTrans trans, Mapper<NSS,PERMS,PERMKEY,ROLES,USERS,USERROLES,DELGS,CERTS,KEYS,REQUEST,HISTORY,ERR,APPROVALS> mapper,Question question) {
133 this.ques = question;
134 func = new Function(trans, question);
135 this.mapper = mapper;
139 /***********************************
141 ***********************************/
144 * @throws DAOException
145 * @see org.onap.aaf.auth.service.AuthzService#createNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
152 errorCodes = { 403,404,406,409 },
153 text = { "Namespace consists of: ",
154 "<ul><li>name - What you want to call this Namespace</li>",
155 "<li>responsible(s) - Person(s) who receive Notifications and approves Requests ",
156 "regarding this Namespace. Companies have Policies as to who may take on ",
157 "this Responsibility. Separate multiple identities with commas</li>",
158 "<li>admin(s) - Person(s) who are allowed to make changes on the namespace, ",
159 "including creating Roles, Permissions and Credentials. Separate multiple ",
160 "identities with commas</li></ul>",
161 "Note: Namespaces are dot-delimited (i.e. com.myCompany.myApp) and must be ",
162 "created with parent credentials (i.e. To create com.myCompany.myApp, you must ",
163 "be an admin of com.myCompany or com"
167 public Result<Void> createNS(final AuthzTrans trans, REQUEST from, NsType type) {
168 final Result<Namespace> rnamespace = mapper.ns(trans, from);
169 final ServiceValidator v = new ServiceValidator();
170 if (v.ns(rnamespace).err()) {
171 return Result.err(Status.ERR_BadData,v.errs());
173 final Namespace namespace = rnamespace.value;
174 final Result<NsDAO.Data> parentNs = ques.deriveNs(trans,namespace.name);
175 if (parentNs.notOK()) {
176 return Result.err(parentNs);
179 // Note: Data validate occurs in func.createNS
180 if (namespace.name.lastIndexOf('.')<0) { // Root Namespace... Function will check if allowed
181 return func.createNS(trans, namespace, false);
184 Result<FutureDAO.Data> fd = mapper.future(trans, NsDAO.TABLE,from,namespace,true,
187 public String get() {
188 return "Create Namespace [" + namespace.name + ']';
192 private Result<NsDAO.Data> rnd;
194 public Result<?> mayChange() {
196 rnd = ques.mayUser(trans, trans.user(), parentNs.value,Access.write);
203 Result<String> rfc = func.createFuture(trans, fd.value, namespace.name, trans.user(),parentNs.value, FUTURE_OP.C);
205 return Result.err(Status.ACC_Future, "NS [%s] is saved for future processing",namespace.name);
207 return Result.err(rfc);
210 return func.createNS(trans, namespace, false);
212 return Result.err(fd);
218 path = "/authz/ns/:ns/admin/:id",
219 params = { "ns|string|true",
223 errorCodes = { 403,404,406,409 },
224 text = { "Add an Identity :id to the list of Admins for the Namespace :ns",
225 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
228 public Result<Void> addAdminNS(AuthzTrans trans, String ns, String id) {
229 return func.addUserRole(trans, id, ns,Question.ADMIN);
234 path = "/authz/ns/:ns/admin/:id",
235 params = { "ns|string|true",
239 errorCodes = { 403,404 },
240 text = { "Remove an Identity :id from the list of Admins for the Namespace :ns",
241 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
244 public Result<Void> delAdminNS(AuthzTrans trans, String ns, String id) {
245 return func.delAdmin(trans,ns,id);
250 path = "/authz/ns/:ns/responsible/:id",
251 params = { "ns|string|true",
255 errorCodes = { 403,404,406,409 },
256 text = { "Add an Identity :id to the list of Responsibles for the Namespace :ns",
257 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)" }
260 public Result<Void> addResponsibleNS(AuthzTrans trans, String ns, String id) {
261 return func.addUserRole(trans,id,ns,Question.OWNER);
266 path = "/authz/ns/:ns/responsible/:id",
267 params = { "ns|string|true",
271 errorCodes = { 403,404 },
272 text = { "Remove an Identity :id to the list of Responsibles for the Namespace :ns",
273 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)",
274 "Note: A namespace must have at least 1 responsible party"
278 public Result<Void> delResponsibleNS(AuthzTrans trans, String ns, String id) {
279 return func.delOwner(trans,ns,id);
283 * @see org.onap.aaf.auth.service.AuthzService#applyModel(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object)
287 path = "/authz/ns/:ns/attrib/:key/:value",
288 params = { "ns|string|true",
290 "value|string|true"},
292 errorCodes = { 403,404,406,409 },
294 "Create an attribute in the Namespace",
295 "You must be given direct permission for key by AAF"
299 public Result<Void> createNsAttrib(AuthzTrans trans, String ns, String key, String value) {
300 TimeTaken tt = trans.start("Create NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
303 final Validator v = new ServiceValidator();
304 if (v.ns(ns).err() ||
306 v.value(value).err()) {
307 return Result.err(Status.ERR_BadData,v.errs());
310 // Check if exists already
311 Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
312 if (rlnsd.notOKorIsEmpty()) {
313 return Result.err(rlnsd);
315 NsDAO.Data nsd = rlnsd.value.get(0);
317 // Check for Existence
318 if (nsd.attrib.get(key)!=null) {
319 return Result.err(Status.ERR_ConflictAlreadyExists, "NS Property %s:%s exists", ns, key);
322 // Check if User may put
323 if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB,
324 ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
325 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
329 nsd.attrib.put(key, value);
330 ques.nsDAO().dao().attribAdd(trans,ns,key,value);
331 ques.nsDAO().invalidate(trans, nsd);
340 path = "/authz/ns/attrib/:key",
341 params = { "key|string|true" },
343 errorCodes = { 403,404 },
345 "Read Attributes for Namespace"
349 public Result<KEYS> readNsByAttrib(AuthzTrans trans, String key) {
351 final Validator v = new ServiceValidator();
352 if (v.nullOrBlank("Key",key).err()) {
353 return Result.err(Status.ERR_BadData,v.errs());
357 if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB,
358 ":"+trans.org().getDomain()+".*:"+key, Question.READ)) {
359 return Result.err(Status.ERR_Denied,"%s may not read NS by Attrib '%s'",trans.user(),key);
362 Result<Set<String>> rsd = ques.nsDAO().dao().readNsByAttrib(trans, key);
364 return Result.err(rsd);
366 return mapper().keys(rsd.value);
372 path = "/authz/ns/:ns/attrib/:key/:value",
373 params = { "ns|string|true",
376 errorCodes = { 403,404 },
378 "Update Value on an existing attribute in the Namespace",
379 "You must be given direct permission for key by AAF"
383 public Result<?> updateNsAttrib(AuthzTrans trans, String ns, String key, String value) {
384 TimeTaken tt = trans.start("Update NsAttrib " + ns + ':' + key + ':' + value, Env.SUB);
387 final Validator v = new ServiceValidator();
388 if (v.ns(ns).err() ||
390 v.value(value).err()) {
391 return Result.err(Status.ERR_BadData,v.errs());
394 // Check if exists already (NS must exist)
395 Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
396 if (rlnsd.notOKorIsEmpty()) {
397 return Result.err(rlnsd);
399 NsDAO.Data nsd = rlnsd.value.get(0);
401 // Check for Existence
402 if (nsd.attrib.get(key)==null) {
403 return Result.err(Status.ERR_NotFound, "NS Property %s:%s exists", ns, key);
406 // Check if User may put
407 if (!ques.isGranted(trans, trans.user(), ROOT_NS, Question.ATTRIB,
408 ":"+trans.org().getDomain()+".*:"+key, Access.write.name())) {
409 return Result.err(Status.ERR_Denied, "%s may not create NS Attrib [%s:%s]", trans.user(),ns, key);
413 nsd.attrib.put(key, value);
414 ques.nsDAO().invalidate(trans, nsd);
415 return ques.nsDAO().update(trans,nsd);
424 path = "/authz/ns/:ns/attrib/:key",
425 params = { "ns|string|true",
428 errorCodes = { 403,404 },
430 "Delete an attribute in the Namespace",
431 "You must be given direct permission for key by AAF"
435 public Result<Void> deleteNsAttrib(AuthzTrans trans, String ns, String key) {
436 TimeTaken tt = trans.start("Delete NsAttrib " + ns + ':' + key, Env.SUB);
439 final Validator v = new ServiceValidator();
440 if (v.nullOrBlank("NS",ns).err() ||
441 v.nullOrBlank("Key",key).err()) {
442 return Result.err(Status.ERR_BadData,v.errs());
445 // Check if exists already
446 Result<List<Data>> rlnsd = ques.nsDAO().read(trans, ns);
447 if (rlnsd.notOKorIsEmpty()) {
448 return Result.err(rlnsd);
450 NsDAO.Data nsd = rlnsd.value.get(0);
452 // Check for Existence
453 if (nsd.attrib.get(key)==null) {
454 return Result.err(Status.ERR_NotFound, "NS Property [%s:%s] does not exist", ns, key);
457 // Check if User may del
458 if (!ques.isGranted(trans, trans.user(), ROOT_NS, "attrib", ":" + ROOT_COMPANY + ".*:"+key, Access.write.name())) {
459 return Result.err(Status.ERR_Denied, "%s may not delete NS Attrib [%s:%s]", trans.user(),ns, key);
463 nsd.attrib.remove(key);
464 ques.nsDAO().dao().attribRemove(trans,ns,key);
465 ques.nsDAO().invalidate(trans, nsd);
474 path = "/authz/nss/:id",
475 params = { "id|string|true" },
477 errorCodes = { 404,406 },
479 "Lists the Owner(s), Admin(s), Description, and Attributes of Namespace :id",
483 public Result<NSS> getNSbyName(AuthzTrans trans, String ns, boolean includeExpired) {
484 final Validator v = new ServiceValidator();
485 if (v.nullOrBlank("NS", ns).err()) {
486 return Result.err(Status.ERR_BadData,v.errs());
489 Result<List<NsDAO.Data>> rlnd = ques.nsDAO().read(trans, ns);
491 if (rlnd.isEmpty()) {
492 return Result.err(Status.ERR_NotFound, "No data found for %s",ns);
494 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.read);
496 return Result.err(rnd);
500 Namespace namespace = new Namespace(rnd.value);
501 Result<List<String>> rd = func.getOwners(trans, namespace.name, includeExpired);
503 namespace.owner = rd.value;
505 rd = func.getAdmins(trans, namespace.name, includeExpired);
507 namespace.admin = rd.value;
510 NSS nss = mapper.newInstance(API.NSS);
511 return mapper.nss(trans, namespace, nss);
513 return Result.err(rlnd);
519 path = "/authz/nss/admin/:id",
520 params = { "id|string|true" },
522 errorCodes = { 403,404 },
523 text = { "Lists all Namespaces where Identity :id is an Admin",
524 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)"
528 public Result<NSS> getNSbyAdmin(AuthzTrans trans, String user, boolean full) {
529 final Validator v = new ServiceValidator();
530 if (v.nullOrBlank("User", user).err()) {
531 return Result.err(Status.ERR_BadData, v.errs());
534 Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".admin", full);
536 return Result.err(rn);
539 return Result.err(Status.ERR_NotFound, "[%s] is not an admin for any namespaces",user);
541 NSS nss = mapper.newInstance(API.NSS);
542 // Note: "loadNamespace" already validates view of Namespace
543 return mapper.nss(trans, rn.value, nss);
548 path = "/authz/nss/either/:id",
549 params = { "id|string|true" },
551 errorCodes = { 403,404 },
552 text = { "Lists all Namespaces where Identity :id is either an Admin or an Owner",
553 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)"
557 public Result<NSS> getNSbyEither(AuthzTrans trans, String user, boolean full) {
558 final Validator v = new ServiceValidator();
559 if (v.nullOrBlank("User", user).err()) {
560 return Result.err(Status.ERR_BadData, v.errs());
563 Result<Collection<Namespace>> rn = loadNamepace(trans, user, null, full);
565 return Result.err(rn);
568 return Result.err(Status.ERR_NotFound, "[%s] is not an admin or owner for any namespaces",user);
570 NSS nss = mapper.newInstance(API.NSS);
571 // Note: "loadNamespace" already validates view of Namespace
572 return mapper.nss(trans, rn.value, nss);
575 private Result<Collection<Namespace>> loadNamepace(AuthzTrans trans, String user, String endsWith, boolean full) {
576 Result<List<UserRoleDAO.Data>> urd = ques.userRoleDAO().readByUser(trans, user);
577 if (urd.notOKorIsEmpty()) {
578 return Result.err(urd);
580 Map<String, Namespace> lm = new HashMap<>();
581 Map<String, Namespace> other = full || endsWith==null?null:new TreeMap<>();
582 for (UserRoleDAO.Data urdd : urd.value) {
584 if (endsWith==null || urdd.role.endsWith(endsWith)) {
585 RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
586 Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
588 Namespace namespace = lm.get(nsd.value.name);
589 if (namespace==null) {
590 namespace = new Namespace(nsd.value);
591 lm.put(namespace.name,namespace);
593 Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
595 namespace.admin=rls.value;
598 rls = func.getOwners(trans, namespace.name, false);
600 namespace.owner=rls.value;
604 } else { // Shortened version. Only Namespace Info available from Role.
605 if (Question.ADMIN.equals(urdd.rname) || Question.OWNER.equals(urdd.rname)) {
606 RoleDAO.Data rd = RoleDAO.Data.decode(urdd);
607 Result<NsDAO.Data> nsd = ques.mayUser(trans, user, rd, Access.read);
609 Namespace namespace = lm.get(nsd.value.name);
610 if (namespace==null) {
612 namespace = other.remove(nsd.value.name);
614 if (namespace==null) {
615 namespace = new Namespace(nsd.value);
616 namespace.admin=new ArrayList<>();
617 namespace.owner=new ArrayList<>();
619 if (endsWith==null || urdd.role.endsWith(endsWith)) {
620 lm.put(namespace.name,namespace);
622 other.put(namespace.name,namespace);
625 if (Question.OWNER.equals(urdd.rname)) {
626 namespace.owner.add(urdd.user);
628 namespace.admin.add(urdd.user);
634 return Result.ok(lm.values());
639 path = "/authz/nss/responsible/:id",
640 params = { "id|string|true" },
642 errorCodes = { 403,404 },
643 text = { "Lists all Namespaces where Identity :id is a Responsible Party",
644 "Note: :id must be fully qualified (i.e. ab1234@people.osaaf.org)"
648 public Result<NSS> getNSbyResponsible(AuthzTrans trans, String user, boolean full) {
649 final Validator v = new ServiceValidator();
650 if (v.nullOrBlank("User", user).err()) {
651 return Result.err(Status.ERR_BadData, v.errs());
653 Result<Collection<Namespace>> rn = loadNamepace(trans, user, ".owner",full);
655 return Result.err(rn);
658 return Result.err(Status.ERR_NotFound, "[%s] is not an owner for any namespaces",user);
660 NSS nss = mapper.newInstance(API.NSS);
661 // Note: "loadNamespace" prevalidates
662 return mapper.nss(trans, rn.value, nss);
667 path = "/authz/nss/children/:id",
668 params = { "id|string|true" },
670 errorCodes = { 403,404 },
671 text = { "Lists all Child Namespaces of Namespace :id",
672 "Note: This is not a cached read"
676 public Result<NSS> getNSsChildren(AuthzTrans trans, String parent) {
677 final Validator v = new ServiceValidator();
678 if (v.nullOrBlank("NS", parent).err()) {
679 return Result.err(Status.ERR_BadData,v.errs());
682 Result<NsDAO.Data> rnd = ques.deriveNs(trans, parent);
684 return Result.err(rnd);
686 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
688 return Result.err(rnd);
691 Set<Namespace> lm = new HashSet<>();
692 Result<List<NsDAO.Data>> rlnd = ques.nsDAO().dao().getChildren(trans, parent);
694 if (rlnd.isEmpty()) {
695 return Result.err(Status.ERR_NotFound, "No data found for %s",parent);
697 for (NsDAO.Data ndd : rlnd.value) {
698 Namespace namespace = new Namespace(ndd);
699 Result<List<String>> rls = func.getAdmins(trans, namespace.name, false);
701 namespace.admin=rls.value;
704 rls = func.getOwners(trans, namespace.name, false);
706 namespace.owner=rls.value;
711 NSS nss = mapper.newInstance(API.NSS);
712 return mapper.nss(trans,lm, nss);
714 return Result.err(rlnd);
724 errorCodes = { 403,404,406 },
725 text = { "Replace the Current Description of a Namespace with a new one"
729 public Result<Void> updateNsDescription(AuthzTrans trans, REQUEST from) {
730 final Result<Namespace> nsd = mapper.ns(trans, from);
731 final ServiceValidator v = new ServiceValidator();
732 if (v.ns(nsd).err()) {
733 return Result.err(Status.ERR_BadData,v.errs());
735 if (v.nullOrBlank("description", nsd.value.description).err()) {
736 return Result.err(Status.ERR_BadData,v.errs());
739 Namespace namespace = nsd.value;
740 Result<List<NsDAO.Data>> rlnd = ques.nsDAO().read(trans, namespace.name);
742 if (rlnd.notOKorIsEmpty()) {
743 return Result.err(Status.ERR_NotFound, "Namespace [%s] does not exist",namespace.name);
746 if (ques.mayUser(trans, trans.user(), rlnd.value.get(0), Access.write).notOK()) {
747 return Result.err(Status.ERR_Denied, "You do not have approval to change %s",namespace.name);
750 Result<Void> rdr = ques.nsDAO().dao().addDescription(trans, namespace.name, namespace.description);
754 return Result.err(rdr);
760 * @throws DAOException
761 * @see org.onap.aaf.auth.service.AuthzService#deleteNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.String)
765 path = "/authz/ns/:ns",
766 params = { "ns|string|true" },
768 errorCodes = { 403,404,424 },
769 text = { "Delete the Namespace :ns. Namespaces cannot normally be deleted when there ",
770 "are still credentials associated with them, but they can be deleted by setting ",
771 "the \"force\" property. To do this: Add 'force=true' as a query parameter",
772 "<p>WARNING: Using force will delete all credentials attached to this namespace. Use with care.</p>"
773 + "if the \"force\" property is set to 'force=move', then Permissions and Roles are not deleted,"
774 + "but are retained, and assigned to the Parent Namespace. 'force=move' is not permitted "
775 + "at or below Application Scope"
779 public Result<Void> deleteNS(AuthzTrans trans, String ns) {
780 return func.deleteNS(trans, ns);
784 /***********************************
786 ***********************************/
790 * @see org.onap.aaf.auth.service.AuthzService#createOrUpdatePerm(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.Object, boolean, java.lang.String, java.lang.String, java.lang.String, java.util.List, java.util.List)
794 path = "/authz/perm",
797 errorCodes = {403,404,406,409},
798 text = { "Permission consists of:",
799 "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
800 + "is being protected</li>",
801 "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
802 + " instance of the type</li>",
803 "<li>action - what kind of action is allowed</li></ul>",
804 "Note: instance and action can be an *"
808 public Result<Void> createPerm(final AuthzTrans trans,REQUEST rreq) {
809 final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
811 final ServiceValidator v = new ServiceValidator();
812 if (v.perm(newPd).err()) {
813 return Result.err(Status.ERR_BadData,v.errs());
816 // User Permission mechanism
817 if(newPd.value.ns.indexOf('@')>0) {
818 PermDAO.Data pdd = newPd.value;
819 if(trans.user().equals(newPd.value.ns)) {
820 CachedPermDAO permDAO = ques.permDAO();
821 Result<List<PermDAO.Data>> rlpdd = permDAO.read(trans, pdd);
823 return Result.err(rlpdd);
825 if(!rlpdd.isEmpty()) {
826 return Result.err(Result.ERR_ConflictAlreadyExists,"Permission already exists");
829 RoleDAO.Data rdd = new RoleDAO.Data();
833 pdd.roles(true).add(rdd.fullName());
834 Result<PermDAO.Data> rpdd = permDAO.create(trans, pdd);
836 return Result.err(rpdd);
839 CachedRoleDAO roleDAO = ques.roleDAO();
840 Result<List<RoleDAO.Data>> rlrdd = roleDAO.read(trans, rdd);
842 return Result.err(rlrdd);
844 if(!rlrdd.isEmpty()) {
845 rdd = rlrdd.value.get(0);
849 String eperm = pdd.encode();
850 rdd.perms(true).add(eperm);
851 Result<Void> rv = roleDAO.update(trans, rdd);
856 CachedUserRoleDAO urDAO = ques.userRoleDAO();
857 UserRoleDAO.Data urdd = new UserRoleDAO.Data();
858 urdd.user = trans.user();
860 urdd.rname = rdd.name;
861 urdd.role = rdd.fullName();
862 Result<List<UserRoleDAO.Data>> rlurdd = urDAO.read(trans, urdd);
864 return Result.err(rlrdd);
865 } else if(rlurdd.isEmpty()) {
866 GregorianCalendar gc = trans.org().expiration(null, Expiration.UserInRole);
868 return Result.err(Result.ERR_Policy,"Organzation does not grant Expiration for UserRole");
870 urdd.expires = gc.getTime();
872 Result<UserRoleDAO.Data> rurdd = urDAO.create(trans, urdd);
873 return Result.err(rurdd);
877 return Result.err(Result.ERR_Security,"Only the User can create User Permissions");
880 // Does Perm Type exist as a Namespace?
881 if(newPd.value.type.isEmpty() || ques.nsDAO().read(trans, newPd.value.fullType()).isOKhasData()) {
882 return Result.err(Status.ERR_ConflictAlreadyExists,
883 "Permission Type exists as a Namespace");
886 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, newPd.value,false,
889 public String get() {
890 return "Create Permission [" +
891 newPd.value.fullType() + '|' +
892 newPd.value.instance + '|' +
893 newPd.value.action + ']';
897 private Result<NsDAO.Data> nsd;
899 public Result<?> mayChange() {
901 nsd = ques.mayUser(trans, trans.user(), newPd.value, Access.write);
907 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, newPd.value.ns);
908 if (nsr.notOKorIsEmpty()) {
909 return Result.err(nsr);
913 Result<String> rfc = func.createFuture(trans,fd.value,
914 newPd.value.fullType() + '|' + newPd.value.instance + '|' + newPd.value.action,
919 return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
922 newPd.value.instance,
925 return Result.err(rfc);
928 return func.createPerm(trans, newPd.value, true);
930 return Result.err(fd);
937 path = "/authz/perms/:type",
938 params = {"type|string|true"},
940 errorCodes = { 404,406 },
941 text = { "List All Permissions that match the :type element of the key" }
944 public Result<PERMS> getPermsByType(AuthzTrans trans, final String permType) {
945 final Validator v = new ServiceValidator();
946 if (v.nullOrBlank("PermType", permType).err()) {
947 return Result.err(Status.ERR_BadData,v.errs());
950 Result<List<PermDAO.Data>> rlpd = ques.getPermsByType(trans, permType);
952 return Result.err(rlpd);
955 // We don't have instance & action for mayUserView... do we want to loop through all returned here as well as in mapper?
956 // Result<NsDAO.Data> r;
957 // if ((r = ques.mayUserViewPerm(trans, trans.user(), permType)).notOK())return Result.err(r);
959 PERMS perms = mapper.newInstance(API.PERMS);
960 if (!rlpd.isEmpty()) {
961 // Note: Mapper will restrict what can be viewed
962 return mapper.perms(trans, rlpd.value, perms, true);
964 return Result.ok(perms);
969 path = "/authz/perms/:type/:instance/:action",
970 params = {"type|string|true",
971 "instance|string|true",
972 "action|string|true"},
974 errorCodes = { 404,406 },
975 text = { "List Permissions that match key; :type, :instance and :action" }
978 public Result<PERMS> getPermsByName(AuthzTrans trans, String type, String instance, String action) {
979 final Validator v = new ServiceValidator();
980 if (v.nullOrBlank("PermType", type).err()
981 || v.nullOrBlank("PermInstance", instance).err()
982 || v.nullOrBlank("PermAction", action).err()) {
983 return Result.err(Status.ERR_BadData,v.errs());
986 Result<List<PermDAO.Data>> rlpd = ques.getPermsByName(trans, type, instance, action);
988 return Result.err(rlpd);
991 PERMS perms = mapper.newInstance(API.PERMS);
992 if (!rlpd.isEmpty()) {
993 // Note: Mapper will restrict what can be viewed
994 return mapper.perms(trans, rlpd.value, perms, true);
996 return Result.ok(perms);
1001 path = "/authz/perms/user/:user",
1002 params = {"user|string|true"},
1004 errorCodes = { 404,406 },
1005 text = { "List All Permissions that match user :user",
1006 "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>"}
1009 public Result<PERMS> getPermsByUser(AuthzTrans trans, String user) {
1010 final Validator v = new ServiceValidator();
1011 if (v.nullOrBlank("User", user).err()) {
1012 return Result.err(Status.ERR_BadData,v.errs());
1015 PermLookup pl = PermLookup.get(trans,ques,user);
1016 Result<List<PermDAO.Data>> rlpd = pl.getPerms(trans.requested(force));
1018 return Result.err(rlpd);
1021 PERMS perms = mapper.newInstance(API.PERMS);
1023 if (rlpd.isEmpty()) {
1024 return Result.ok(perms);
1026 // Note: Mapper will restrict what can be viewed
1027 // if user is the same as that which is looked up, no filtering is required
1028 return mapper.perms(trans, rlpd.value,
1030 !user.equals(trans.user()));
1035 path = "/authz/perms/user/:user/scope/:scope",
1036 params = {"user|string|true","scope|string|true"},
1038 errorCodes = { 404,406 },
1039 text = { "List All Permissions that match user :user, filtered by NS (Scope)",
1040 "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
1041 "<p>'scope' must be expressed as NSs separated by ':'</p>"
1045 public Result<PERMS> getPermsByUserScope(AuthzTrans trans, String user, String[] scopes) {
1046 final Validator v = new ServiceValidator();
1047 if (v.nullOrBlank("User", user).err()) {
1048 return Result.err(Status.ERR_BadData,v.errs());
1051 Result<List<PermDAO.Data>> rlpd = ques.getPermsByUser(trans, user, trans.requested(force));
1053 return Result.err(rlpd);
1056 PERMS perms = mapper.newInstance(API.PERMS);
1058 if (rlpd.isEmpty()) {
1059 return Result.ok(perms);
1061 // Note: Mapper will restrict what can be viewed
1062 // if user is the same as that which is looked up, no filtering is required
1063 return mapper.perms(trans, rlpd.value,
1066 !user.equals(trans.user()));
1071 path = "/authz/perms/user/:user",
1072 params = {"user|string|true"},
1074 errorCodes = { 404,406 },
1075 text = { "List All Permissions that match user :user",
1076 "<p>'user' must be expressed as full identity (ex: id@full.domain.com)</p>",
1078 "Present Queries as one or more Permissions (see ContentType Links below for format).",
1080 "If the Caller is Granted this specific Permission, and the Permission is valid",
1081 " for the User, it will be included in response Permissions, along with",
1082 " all the normal permissions on the 'GET' version of this call. If it is not",
1083 " valid, or Caller does not have permission to see, it will be removed from the list",
1085 " *Note: This design allows you to make one call for all expected permissions",
1086 " The permission to be included MUST be:",
1087 " <user namespace>.access|:<ns|role|perm>[:key]|<create|read|write>",
1089 " com.att.myns.access|:ns|write",
1090 " com.att.myns.access|:role:myrole|create",
1091 " com.att.myns.access|:perm:mytype:myinstance:myaction|read",
1096 public Result<PERMS> getPermsByUser(AuthzTrans trans, PERMS _perms, String user) {
1097 PERMS perms = _perms;
1098 final Validator v = new ServiceValidator();
1099 if (v.nullOrBlank("User", user).err()) {
1100 return Result.err(Status.ERR_BadData,v.errs());
1104 PermLookup pl = PermLookup.get(trans,ques,user);
1105 Result<List<PermDAO.Data>> rlpd = pl.getPerms(trans.requested(force));
1107 return Result.err(rlpd);
1111 1) See if allowed to query
1112 2) See if User is allowed
1114 Result<List<PermDAO.Data>> in = mapper.perms(trans, perms);
1115 if (in.isOKhasData()) {
1116 List<PermDAO.Data> out = rlpd.value;
1118 for (PermDAO.Data pdd : in.value) {
1120 if ("access".equals(pdd.type)) {
1121 Access access = Access.valueOf(pdd.action);
1122 String[] mdkey = Split.splitTrim(':',pdd.instance);
1123 if (mdkey.length>1) {
1124 String type = mdkey[1];
1125 if ("role".equals(type)) {
1126 if (mdkey.length>2) {
1127 RoleDAO.Data rdd = new RoleDAO.Data();
1130 ok = ques.mayUser(trans, trans.user(), rdd, Access.read).isOK() && ques.mayUser(trans, user, rdd , access).isOK();
1132 } else if ("perm".equals(type)) {
1133 if (mdkey.length>4) { // also need instance/action
1134 PermDAO.Data p = new PermDAO.Data();
1137 p.instance=mdkey[3];
1139 ok = ques.mayUser(trans, trans.user(), p, Access.read).isOK() && ques.mayUser(trans, user, p , access).isOK();
1141 } else if ("ns".equals(type)) {
1142 NsDAO.Data ndd = new NsDAO.Data();
1144 ok = ques.mayUser(trans, trans.user(), ndd, Access.read).isOK() && ques.mayUser(trans, user, ndd , access).isOK();
1154 perms = mapper.newInstance(API.PERMS);
1155 if (rlpd.isEmpty()) {
1156 return Result.ok(perms);
1158 // Note: Mapper will restrict what can be viewed
1159 // if user is the same as that which is looked up, no filtering is required
1160 return mapper.perms(trans, rlpd.value,
1162 !user.equals(trans.user()));
1167 path = "/authz/perms/role/:role",
1168 params = {"role|string|true"},
1170 errorCodes = { 404,406 },
1171 text = { "List All Permissions that are granted to :role" }
1174 public Result<PERMS> getPermsByRole(AuthzTrans trans,String role) {
1175 final Validator v = new ServiceValidator();
1176 if (v.nullOrBlank("Role", role).err()) {
1177 return Result.err(Status.ERR_BadData,v.errs());
1180 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques,role);
1182 return Result.err(rrdd);
1185 Result<NsDAO.Data> r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
1187 return Result.err(r);
1190 PERMS perms = mapper.newInstance(API.PERMS);
1192 Result<List<PermDAO.Data>> rlpd = ques.getPermsByRole(trans, role, trans.requested(force));
1193 if (rlpd.isOKhasData()) {
1194 // Note: Mapper will restrict what can be viewed
1195 return mapper.perms(trans, rlpd.value, perms, true);
1197 return Result.ok(perms);
1202 path = "/authz/perms/ns/:ns",
1203 params = {"ns|string|true"},
1205 errorCodes = { 404,406 },
1206 text = { "List All Permissions that are in Namespace :ns" }
1209 public Result<PERMS> getPermsByNS(AuthzTrans trans,String ns) {
1210 final Validator v = new ServiceValidator();
1211 if (v.nullOrBlank("NS", ns).err()) {
1212 return Result.err(Status.ERR_BadData,v.errs());
1215 Result<NsDAO.Data> rnd = ques.deriveNs(trans, ns);
1217 return Result.err(rnd);
1220 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
1222 return Result.err(rnd);
1225 Result<List<PermDAO.Data>> rlpd = ques.permDAO().readNS(trans, ns);
1227 return Result.err(rlpd);
1230 PERMS perms = mapper.newInstance(API.PERMS);
1231 if (!rlpd.isEmpty()) {
1232 // Note: Mapper will restrict what can be viewed
1233 return mapper.perms(trans, rlpd.value,perms, true);
1235 return Result.ok(perms);
1240 path = "/authz/perm/:type/:instance/:action",
1241 params = {"type|string|true",
1242 "instance|string|true",
1243 "action|string|true"},
1245 errorCodes = { 404,406, 409 },
1246 text = { "Rename the Permission referenced by :type :instance :action, and "
1247 + "rename (copy/delete) to the Permission described in PermRequest" }
1250 public Result<Void> renamePerm(final AuthzTrans trans,REQUEST rreq, String origType, String origInstance, String origAction) {
1251 final Result<PermDAO.Data> newPd = mapper.perm(trans, rreq);
1252 final ServiceValidator v = new ServiceValidator();
1253 if (v.perm(newPd).err()) {
1254 return Result.err(Status.ERR_BadData,v.errs());
1257 if (ques.mayUser(trans, trans.user(), newPd.value,Access.write).notOK()) {
1258 return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1259 newPd.value.ns,newPd.value.type,newPd.value.instance,newPd.value.action);
1262 Result<NsSplit> nss = ques.deriveNsSplit(trans, origType);
1263 Result<List<PermDAO.Data>> origRlpd = ques.permDAO().read(trans, nss.value.ns, nss.value.name, origInstance, origAction);
1265 if (origRlpd.notOKorIsEmpty()) {
1266 return Result.err(Status.ERR_PermissionNotFound,
1267 "Permission [%s|%s|%s] does not exist",
1268 origType,origInstance,origAction);
1271 PermDAO.Data origPd = origRlpd.value.get(0);
1273 if (!origPd.ns.equals(newPd.value.ns)) {
1274 return Result.err(Status.ERR_Denied, "Cannot change namespace with rename command. " +
1275 "<new type> must start with [" + origPd.ns + "]");
1278 if ( origPd.type.equals(newPd.value.type) &&
1279 origPd.action.equals(newPd.value.action) &&
1280 origPd.instance.equals(newPd.value.instance) ) {
1281 return Result.err(Status.ERR_ConflictAlreadyExists, "New Permission must be different than original permission");
1284 Set<String> origRoles = origPd.roles(false);
1285 if (!origRoles.isEmpty()) {
1286 Set<String> roles = newPd.value.roles(true);
1287 for (String role : origPd.roles) {
1292 newPd.value.description = origPd.description;
1294 Result<Void> rv = null;
1296 rv = func.createPerm(trans, newPd.value, false);
1298 rv = func.deletePerm(trans, origPd, true, false);
1305 path = "/authz/perm",
1308 errorCodes = { 404,406 },
1309 text = { "Add Description Data to Perm" }
1312 public Result<Void> updatePermDescription(AuthzTrans trans, REQUEST from) {
1313 final Result<PermDAO.Data> pd = mapper.perm(trans, from);
1314 final ServiceValidator v = new ServiceValidator();
1315 if (v.perm(pd).err()) {
1316 return Result.err(Status.ERR_BadData,v.errs());
1318 if (v.nullOrBlank("description", pd.value.description).err()) {
1319 return Result.err(Status.ERR_BadData,v.errs());
1321 final PermDAO.Data perm = pd.value;
1322 if (ques.permDAO().read(trans, perm.ns, perm.type, perm.instance,perm.action).notOKorIsEmpty()) {
1323 return Result.err(Status.ERR_NotFound, "Permission [%s.%s|%s|%s] does not exist",
1324 perm.ns,perm.type,perm.instance,perm.action);
1327 if (ques.mayUser(trans, trans.user(), perm, Access.write).notOK()) {
1328 return Result.err(Status.ERR_Denied, "You do not have approval to change Permission [%s.%s|%s|%s]",
1329 perm.ns,perm.type,perm.instance,perm.action);
1332 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, pd.value.ns);
1333 if (nsr.notOKorIsEmpty()) {
1334 return Result.err(nsr);
1337 Result<Void> rdr = ques.permDAO().addDescription(trans, perm.ns, perm.type, perm.instance,
1338 perm.action, perm.description);
1342 return Result.err(rdr);
1349 path = "/authz/role/perm",
1352 errorCodes = {403,404,406,409},
1353 text = { "Set a permission's roles to roles given" }
1357 public Result<Void> resetPermRoles(final AuthzTrans trans, REQUEST rreq) {
1358 final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
1359 if (updt.notOKorIsEmpty()) {
1360 return Result.err(updt);
1363 final ServiceValidator v = new ServiceValidator();
1364 if (v.perm(updt).err()) {
1365 return Result.err(Status.ERR_BadData,v.errs());
1368 Result<NsDAO.Data> nsd = ques.mayUser(trans, trans.user(), updt.value, Access.write);
1370 return Result.err(nsd);
1373 // Read full set to get CURRENT values
1374 Result<List<PermDAO.Data>> rcurr = ques.permDAO().read(trans,
1377 updt.value.instance,
1380 if (rcurr.notOKorIsEmpty()) {
1381 return Result.err(Status.ERR_PermissionNotFound,
1382 "Permission [%s.%s|%s|%s] does not exist",
1383 updt.value.ns,updt.value.type,updt.value.instance,updt.value.action);
1386 // Create a set of Update Roles, which are in Internal Format
1387 Set<String> updtRoles = new HashSet<>();
1388 Result<NsSplit> nss;
1389 for (String role : updt.value.roles(false)) {
1390 nss = ques.deriveNsSplit(trans, role);
1392 updtRoles.add(nss.value.ns + '|' + nss.value.name);
1394 trans.error().log(nss.errorString());
1398 Result<Void> rv = null;
1400 for (PermDAO.Data curr : rcurr.value) {
1401 Set<String> currRoles = curr.roles(false);
1402 // must add roles to this perm, and add this perm to each role
1403 // in the update, but not in the current
1404 for (String role : updtRoles) {
1405 if (!currRoles.contains(role)) {
1406 Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1407 if (key.isOKhasData()) {
1408 Result<List<RoleDAO.Data>> rrd = ques.roleDAO().read(trans, key.value);
1409 if (rrd.isOKhasData()) {
1410 for (RoleDAO.Data r : rrd.value) {
1411 rv = func.addPermToRole(trans, r, curr, false);
1412 if (rv.notOK() && rv.status!=Result.ERR_ConflictAlreadyExists) {
1413 return Result.err(rv);
1417 return Result.err(rrd);
1422 // similarly, must delete roles from this perm, and delete this perm from each role
1423 // in the update, but not in the current
1424 for (String role : currRoles) {
1425 if (!updtRoles.contains(role)) {
1426 Result<RoleDAO.Data> key = RoleDAO.Data.decode(trans, ques, role);
1427 if (key.isOKhasData()) {
1428 Result<List<RoleDAO.Data>> rdd = ques.roleDAO().read(trans, key.value);
1429 if (rdd.isOKhasData()) {
1430 for (RoleDAO.Data r : rdd.value) {
1431 rv = func.delPermFromRole(trans, r, curr, true);
1432 if (rv.notOK() && rv.status!=Status.ERR_PermissionNotFound) {
1433 return Result.err(rv);
1441 return rv==null?Result.ok():rv;
1446 path = "/authz/perm",
1449 errorCodes = { 404,406 },
1450 text = { "Delete the Permission referenced by PermKey.",
1451 "You cannot normally delete a permission which is still granted to roles,",
1452 "however the \"force\" property allows you to do just that. To do this: Add",
1453 "'force=true' as a query parameter.",
1454 "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>" }
1457 public Result<Void> deletePerm(final AuthzTrans trans, REQUEST from) {
1458 Result<PermDAO.Data> pd = mapper.perm(trans, from);
1460 return Result.err(pd);
1462 final ServiceValidator v = new ServiceValidator();
1463 if (v.nullOrBlank(pd.value).err()) {
1464 return Result.err(Status.ERR_BadData,v.errs());
1466 final PermDAO.Data perm = pd.value;
1467 if (ques.permDAO().read(trans, perm).notOKorIsEmpty()) {
1468 return Result.err(Status.ERR_PermissionNotFound, "Permission [%s.%s|%s|%s] does not exist",
1469 perm.ns,perm.type,perm.instance,perm.action );
1472 Result<FutureDAO.Data> fd = mapper.future(trans,PermDAO.TABLE,from,perm,false,
1475 public String get() {
1476 return "Delete Permission [" + perm.fullPerm() + ']';
1480 private Result<NsDAO.Data> nsd;
1482 public Result<?> mayChange() {
1484 nsd = ques.mayUser(trans, trans.user(), perm, Access.write);
1492 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, perm.ns);
1493 if (nsr.notOKorIsEmpty()) {
1494 return Result.err(nsr);
1497 Result<String> rfc = func.createFuture(trans, fd.value,
1498 perm.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
1500 return Result.err(Status.ACC_Future, "Perm Deletion [%s] is saved for future processing",perm.encode());
1502 return Result.err(rfc);
1504 case Status.ACC_Now:
1505 return func.deletePerm(trans,perm,trans.requested(force), false);
1507 return Result.err(fd);
1513 path = "/authz/perm/:name/:type/:action",
1514 params = {"type|string|true",
1515 "instance|string|true",
1516 "action|string|true"},
1518 errorCodes = { 404,406 },
1519 text = { "Delete the Permission referenced by :type :instance :action",
1520 "You cannot normally delete a permission which is still granted to roles,",
1521 "however the \"force\" property allows you to do just that. To do this: Add",
1522 "'force=true' as a query parameter",
1523 "<p>WARNING: Using force will ungrant this permission from all roles. Use with care.</p>"}
1526 public Result<Void> deletePerm(AuthzTrans trans, String type, String instance, String action) {
1527 final Validator v = new ServiceValidator();
1528 if (v.nullOrBlank("Type",type)
1529 .nullOrBlank("Instance",instance)
1530 .nullOrBlank("Action",action)
1532 return Result.err(Status.ERR_BadData,v.errs());
1535 Result<PermDAO.Data> pd = ques.permFrom(trans, type, instance, action);
1537 return func.deletePerm(trans, pd.value, trans.requested(force), false);
1539 return Result.err(pd);
1543 /***********************************
1545 ***********************************/
1548 path = "/authz/role",
1551 errorCodes = {403,404,406,409},
1554 "Roles are part of Namespaces",
1556 "<ul><li>org.onap.aaf - The team that created and maintains AAF</li>",
1557 "Roles do not include implied permissions for an App. Instead, they contain explicit Granted Permissions by any Namespace in AAF (See Permissions)",
1558 "Restrictions on Role Names:",
1559 "<ul><li>Must start with valid Namespace name, terminated by . (dot/period)</li>",
1560 "<li>Allowed Characters are a-zA-Z0-9._-</li>",
1561 "<li>role names are Case Sensitive</li></ul>",
1562 "The right questions to ask for defining and populating a Role in AAF, therefore, are:",
1563 "<ul><li>'What Job Function does this represent?'</li>",
1564 "<li>'Does this person perform this Job Function?'</li></ul>" }
1568 public Result<Void> createRole(final AuthzTrans trans, REQUEST from) {
1569 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1570 // Does Perm Type exist as a Namespace?
1571 if(rd.value.name.isEmpty() || ques.nsDAO().read(trans, rd.value.fullName()).isOKhasData()) {
1572 return Result.err(Status.ERR_ConflictAlreadyExists,
1573 "Role exists as a Namespace");
1575 final ServiceValidator v = new ServiceValidator();
1576 if (v.role(rd).err()) {
1577 return Result.err(Status.ERR_BadData,v.errs());
1579 final RoleDAO.Data role = rd.value;
1580 if (ques.roleDAO().read(trans, role.ns, role.name).isOKhasData()) {
1581 return Result.err(Status.ERR_ConflictAlreadyExists, "Role [" + role.fullName() + "] already exists");
1584 Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
1587 public String get() {
1588 return "Create Role [" +
1589 rd.value.fullName() +
1594 private Result<NsDAO.Data> nsd;
1596 public Result<?> mayChange() {
1598 nsd = ques.mayUser(trans, trans.user(), role, Access.write);
1604 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
1605 if (nsr.notOKorIsEmpty()) {
1606 return Result.err(nsr);
1611 Result<String> rfc = func.createFuture(trans, fd.value,
1612 role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.C);
1614 return Result.err(Status.ACC_Future, "Role [%s.%s] is saved for future processing",
1618 return Result.err(rfc);
1620 case Status.ACC_Now:
1621 Result<RoleDAO.Data> rdr = ques.roleDAO().create(trans, role);
1625 return Result.err(rdr);
1628 return Result.err(fd);
1633 * @see org.onap.aaf.auth.service.AuthzService#getRolesByName(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1637 path = "/authz/roles/:role",
1638 params = {"role|string|true"},
1640 errorCodes = {404,406},
1641 text = { "List Roles that match :role",
1642 "Note: You must have permission to see any given role"
1646 public Result<ROLES> getRolesByName(AuthzTrans trans, String role) {
1647 final Validator v = new ServiceValidator();
1648 if (v.nullOrBlank("Role", role).err()) {
1649 return Result.err(Status.ERR_BadData,v.errs());
1652 // Determine if User can ask this question
1653 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
1654 if (rrdd.isOKhasData()) {
1655 Result<NsDAO.Data> r;
1656 if ((r = ques.mayUser(trans, trans.user(), rrdd.value, Access.read)).notOK()) {
1657 return Result.err(r);
1660 return Result.err(rrdd);
1664 int query = role.indexOf('?');
1665 Result<List<RoleDAO.Data>> rlrd = ques.getRolesByName(trans, query<0?role:role.substring(0, query));
1667 // Note: Mapper will restrict what can be viewed
1668 ROLES roles = mapper.newInstance(API.ROLES);
1669 return mapper.roles(trans, rlrd.value, roles, true);
1671 return Result.err(rlrd);
1676 * @see org.onap.aaf.auth.service.AuthzService#getRolesByUser(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1680 path = "/authz/roles/user/:name",
1681 params = {"name|string|true"},
1683 errorCodes = {404,406},
1684 text = { "List all Roles that match user :name",
1685 "'user' must be expressed as full identity (ex: id@full.domain.com)",
1686 "Note: You must have permission to see any given role"
1691 public Result<ROLES> getRolesByUser(AuthzTrans trans, String user) {
1692 final Validator v = new ServiceValidator();
1693 if (v.nullOrBlank("User", user).err()) {
1694 return Result.err(Status.ERR_BadData,v.errs());
1697 ROLES roles = mapper.newInstance(API.ROLES);
1698 // Get list of roles per user, then add to Roles as we go
1699 Result<List<RoleDAO.Data>> rlrd;
1700 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, user);
1701 if (rlurd.isOKhasData()) {
1702 for (UserRoleDAO.Data urd : rlurd.value ) {
1703 rlrd = ques.roleDAO().read(trans, urd.ns,urd.rname);
1704 // Note: Mapper will restrict what can be viewed
1705 // if user is the same as that which is looked up, no filtering is required
1706 if (rlrd.isOKhasData()) {
1707 mapper.roles(trans, rlrd.value,roles, !user.equals(trans.user()));
1711 return Result.ok(roles);
1716 * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1720 path = "/authz/roles/ns/:ns",
1721 params = {"ns|string|true"},
1723 errorCodes = {404,406},
1724 text = { "List all Roles for the Namespace :ns",
1725 "Note: You must have permission to see any given role"
1730 public Result<ROLES> getRolesByNS(AuthzTrans trans, String ns) {
1731 final Validator v = new ServiceValidator();
1732 if (v.nullOrBlank("NS", ns).err()) {
1733 return Result.err(Status.ERR_BadData,v.errs());
1736 // check if user is allowed to view NS
1737 Result<NsDAO.Data> rnsd = ques.deriveNs(trans, ns);
1739 return Result.err(rnsd);
1741 rnsd = ques.mayUser(trans, trans.user(), rnsd.value, Access.read);
1743 return Result.err(rnsd);
1746 TimeTaken tt = trans.start("MAP Roles by NS to Roles", Env.SUB);
1748 ROLES roles = mapper.newInstance(API.ROLES);
1749 // Get list of roles per user, then add to Roles as we go
1750 Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().readNS(trans, ns);
1752 if (!rlrd.isEmpty()) {
1753 // Note: Mapper doesn't need to restrict what can be viewed, because we did it already.
1754 mapper.roles(trans,rlrd.value,roles,false);
1756 return Result.ok(roles);
1758 return Result.err(rlrd);
1767 * @see org.onap.aaf.auth.service.AuthzService#getRolesByNS(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
1771 path = "/authz/roles/name/:name",
1772 params = {"name|string|true"},
1774 errorCodes = {404,406},
1775 text = { "List all Roles for only the Name of Role (without Namespace)",
1776 "Note: You must have permission to see any given role"
1780 public Result<ROLES> getRolesByNameOnly(AuthzTrans trans, String name) {
1781 final Validator v = new ServiceValidator();
1782 if (v.nullOrBlank("Name", name).err()) {
1783 return Result.err(Status.ERR_BadData,v.errs());
1786 // User Mapper to make sure user is allowed to view NS
1788 TimeTaken tt = trans.start("MAP Roles by Name to Roles", Env.SUB);
1790 ROLES roles = mapper.newInstance(API.ROLES);
1791 // Get list of roles per user, then add to Roles as we go
1792 Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().readName(trans, name);
1794 if (!rlrd.isEmpty()) {
1795 // Note: Mapper will restrict what can be viewed
1796 mapper.roles(trans,rlrd.value,roles,true);
1798 return Result.ok(roles);
1800 return Result.err(rlrd);
1809 path = "/authz/roles/perm/:type/:instance/:action",
1810 params = {"type|string|true",
1811 "instance|string|true",
1812 "action|string|true"},
1814 errorCodes = {404,406},
1815 text = { "Find all Roles containing the given Permission." +
1816 "Permission consists of:",
1817 "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1818 + "is being protected</li>",
1819 "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1820 + " instance of the type</li>",
1821 "<li>action - what kind of action is allowed</li></ul>",
1822 "Notes: instance and action can be an *",
1823 " You must have permission to see any given role"
1828 public Result<ROLES> getRolesByPerm(AuthzTrans trans, String type, String instance, String action) {
1829 final Validator v = new ServiceValidator();
1830 if (v.permType(type)
1831 .permInstance(instance)
1834 return Result.err(Status.ERR_BadData,v.errs());
1837 TimeTaken tt = trans.start("Map Perm Roles Roles", Env.SUB);
1839 ROLES roles = mapper.newInstance(API.ROLES);
1840 // Get list of roles per user, then add to Roles as we go
1841 Result<NsSplit> nsSplit = ques.deriveNsSplit(trans, type);
1842 if (nsSplit.isOK()) {
1843 PermDAO.Data pdd = new PermDAO.Data(nsSplit.value, instance, action);
1845 if ((res=ques.mayUser(trans, trans.user(), pdd, Question.Access.read)).notOK()) {
1846 return Result.err(res);
1849 Result<List<PermDAO.Data>> pdlr = ques.permDAO().read(trans, pdd);
1850 if (pdlr.isOK())for (PermDAO.Data pd : pdlr.value) {
1851 Result<List<RoleDAO.Data>> rlrd;
1852 for (String r : pd.roles) {
1853 Result<String[]> rs = RoleDAO.Data.decodeToArray(trans, ques, r);
1855 rlrd = ques.roleDAO().read(trans, rs.value[0],rs.value[1]);
1856 // Note: Mapper will restrict what can be viewed
1857 if (rlrd.isOKhasData()) {
1858 mapper.roles(trans,rlrd.value,roles,true);
1864 return Result.ok(roles);
1872 path = "/authz/role",
1875 errorCodes = {404,406},
1876 text = { "Add Description Data to a Role" }
1880 public Result<Void> updateRoleDescription(AuthzTrans trans, REQUEST from) {
1881 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
1882 final ServiceValidator v = new ServiceValidator();
1883 if (v.role(rd).err()) {
1884 return Result.err(Status.ERR_BadData,v.errs());
1886 if (v.nullOrBlank("description", rd.value.description).err()) {
1887 return Result.err(Status.ERR_BadData,v.errs());
1890 final RoleDAO.Data role = rd.value;
1891 if (ques.roleDAO().read(trans, role.ns, role.name).notOKorIsEmpty()) {
1892 return Result.err(Status.ERR_NotFound, "Role [" + role.fullName() + "] does not exist");
1895 if (ques.mayUser(trans, trans.user(), role, Access.write).notOK()) {
1896 return Result.err(Status.ERR_Denied, "You do not have approval to change " + role.fullName());
1899 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
1900 if (nsr.notOKorIsEmpty()) {
1901 return Result.err(nsr);
1904 Result<Void> rdr = ques.roleDAO().addDescription(trans, role.ns, role.name, role.description);
1908 return Result.err(rdr);
1915 path = "/authz/role/perm",
1918 errorCodes = {403,404,406,409},
1919 text = { "Grant a Permission to a Role",
1920 "Permission consists of:",
1921 "<ul><li>type - a Namespace qualified identifier specifying what kind of resource "
1922 + "is being protected</li>",
1923 "<li>instance - a key, possibly multi-dimensional, that identifies a specific "
1924 + " instance of the type</li>",
1925 "<li>action - what kind of action is allowed</li></ul>",
1926 "Note: instance and action can be an *",
1927 "Note: Using the \"force\" property will create the Permission, if it doesn't exist AND the requesting " +
1928 " ID is allowed to create. It will then grant",
1929 " the permission to the role in one step. To do this: add 'force=true' as a query parameter."
1934 public Result<Void> addPermToRole(final AuthzTrans trans, REQUEST rreq) {
1935 // Translate Request into Perm and Role Objects
1936 final Result<PermDAO.Data> rpd = mapper.permFromRPRequest(trans, rreq);
1937 if (rpd.notOKorIsEmpty()) {
1938 return Result.err(rpd);
1940 final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
1941 if (rrd.notOKorIsEmpty()) {
1942 return Result.err(rrd);
1945 // Validate Role and Perm values
1946 final ServiceValidator v = new ServiceValidator();
1947 if (v.perm(rpd.value)
1950 return Result.err(Status.ERR_BadData,v.errs());
1953 Result<List<RoleDAO.Data>> rlrd = ques.roleDAO().read(trans, rrd.value.ns, rrd.value.name);
1954 if (rlrd.notOKorIsEmpty()) {
1955 return Result.err(Status.ERR_RoleNotFound, "Role [%s] does not exist", rrd.value.fullName());
1958 // Check Status of Data in DB (does it exist)
1959 Result<List<PermDAO.Data>> rlpd = ques.permDAO().read(trans, rpd.value.ns,
1960 rpd.value.type, rpd.value.instance, rpd.value.action);
1961 PermDAO.Data createPerm = null; // if not null, create first
1962 if (rlpd.notOKorIsEmpty()) { // Permission doesn't exist
1963 if (trans.requested(force)) {
1964 // Remove roles from perm data object so we just create the perm here
1965 createPerm = rpd.value;
1966 createPerm.roles.clear();
1968 return Result.err(Status.ERR_PermissionNotFound,"Permission [%s.%s|%s|%s] does not exist",
1969 rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action);
1972 if (rlpd.value.get(0).roles(false).contains(rrd.value.encode())) {
1973 return Result.err(Status.ERR_ConflictAlreadyExists,
1974 "Permission [%s.%s|%s|%s] already granted to Role [%s.%s]",
1975 rpd.value.ns,rpd.value.type,rpd.value.instance,rpd.value.action,
1976 rrd.value.ns,rrd.value.name
1982 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, rpd.value,true, // Allow grants to create Approvals
1985 public String get() {
1986 return "Grant Permission [" + rpd.value.fullPerm() + ']' +
1987 " to Role [" + rrd.value.fullName() + "]";
1991 private Result<NsDAO.Data> nsd;
1993 public Result<?> mayChange() {
1995 nsd = ques.mayUser(trans, trans.user(), rpd.value, Access.write);
1997 trans.requested(REQD_TYPE.future,true);
2003 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rpd.value.ns);
2004 if (nsr.notOKorIsEmpty()) {
2005 return Result.err(nsr);
2009 Result<String> rfc = func.createFuture(trans,fd.value,
2010 rpd.value.fullPerm(),
2015 return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
2021 return Result.err(rfc);
2023 case Status.ACC_Now:
2024 Result<Void> rv = null;
2025 if (createPerm!=null) {// has been validated for creating
2026 rv = func.createPerm(trans, createPerm, false);
2028 if (rv==null || rv.isOK()) {
2029 rv = func.addPermToRole(trans, rrd.value, rpd.value, false);
2033 return Result.err(fd);
2039 * Delete Perms from Roles (UnGrant)
2041 * @param roleFullName
2046 path = "/authz/role/:role/perm",
2047 params = {"role|string|true"},
2049 errorCodes = {404,406},
2050 text = { "Ungrant a permission from Role :role" }
2054 public Result<Void> delPermFromRole(final AuthzTrans trans, REQUEST rreq) {
2055 final Result<PermDAO.Data> updt = mapper.permFromRPRequest(trans, rreq);
2056 if (updt.notOKorIsEmpty()) {
2057 return Result.err(updt);
2059 final Result<RoleDAO.Data> rrd = mapper.roleFromRPRequest(trans, rreq);
2060 if (rrd.notOKorIsEmpty()) {
2061 return Result.err(rrd);
2064 final ServiceValidator v = new ServiceValidator();
2065 if (v.nullOrBlank(updt.value)
2066 .nullOrBlank(rrd.value)
2068 return Result.err(Status.ERR_BadData,v.errs());
2071 return delPermFromRole(trans, updt.value,rrd.value, rreq);
2074 private Result<Void> delPermFromRole(final AuthzTrans trans, PermDAO.Data pdd, RoleDAO.Data rdd, REQUEST rreq) {
2075 Result<List<PermDAO.Data>> rlpd = ques.permDAO().read(trans, pdd.ns, pdd.type,
2076 pdd.instance, pdd.action);
2078 if (rlpd.notOKorIsEmpty()) {
2079 return Result.err(Status.ERR_PermissionNotFound,
2080 "Permission [%s.%s|%s|%s] does not exist",
2081 pdd.ns,pdd.type,pdd.instance,pdd.action);
2084 Result<FutureDAO.Data> fd = mapper.future(trans, PermDAO.TABLE, rreq, pdd,true, // allow ungrants requests
2087 public String get() {
2088 return "Ungrant Permission [" + pdd.fullPerm() + ']' +
2089 " from Role [" + rdd.fullName() + "]";
2093 private Result<NsDAO.Data> nsd;
2095 public Result<?> mayChange() {
2097 nsd = ques.mayUser(trans, trans.user(), pdd, Access.write);
2102 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, pdd.ns);
2103 if (nsr.notOKorIsEmpty()) {
2104 return Result.err(nsr);
2108 Result<String> rfc = func.createFuture(trans,fd.value,
2115 return Result.err(Status.ACC_Future, "Perm [%s.%s|%s|%s] is saved for future processing",
2121 return Result.err(rfc);
2123 case Status.ACC_Now:
2124 return func.delPermFromRole(trans, rdd, pdd, false);
2126 return Result.err(fd);
2133 path = "/authz/role/:role/perm/:type/:instance/:action",
2134 params = {"role|string|true",
2135 "perm type|string|true",
2136 "perm instance|string|true",
2137 "perm action|string|true"
2140 errorCodes = {404,406},
2141 text = { "Ungrant a single permission from Role :role with direct key" }
2145 public Result<Void> delPermFromRole(AuthzTrans trans, String role, String type, String instance, String action) {
2146 Result<Data> rpns = ques.deriveNs(trans, type);
2147 if (rpns.notOKorIsEmpty()) {
2148 return Result.err(rpns);
2151 final Validator v = new ServiceValidator();
2153 .permType(rpns.value.name,rpns.value.parent)
2154 .permInstance(instance)
2157 return Result.err(Status.ERR_BadData,v.errs());
2160 Result<Data> rrns = ques.deriveNs(trans, role);
2161 if (rrns.notOKorIsEmpty()) {
2162 return Result.err(rrns);
2165 final Result<List<RoleDAO.Data>> rrd = ques.roleDAO().read(trans, rrns.value.parent, rrns.value.name);
2166 if (rrd.notOKorIsEmpty()) {
2167 return Result.err(rrd);
2170 final Result<List<PermDAO.Data>> rpd = ques.permDAO().read(trans, rpns.value.parent, rpns.value.name, instance, action);
2171 if (rpd.notOKorIsEmpty()) {
2172 return Result.err(rpd);
2176 return delPermFromRole(trans,rpd.value.get(0), rrd.value.get(0), mapper.ungrantRequest(trans, role, type, instance, action));
2181 path = "/authz/role/:role",
2182 params = {"role|string|true"},
2184 errorCodes = {404,406},
2185 text = { "Delete the Role named :role"}
2189 public Result<Void> deleteRole(AuthzTrans trans, String role) {
2190 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
2191 if (rrdd.isOKhasData()) {
2192 final ServiceValidator v = new ServiceValidator();
2193 if (v.nullOrBlank(rrdd.value).err()) {
2194 return Result.err(Status.ERR_BadData,v.errs());
2196 return func.deleteRole(trans, rrdd.value, false, false);
2198 return Result.err(rrdd);
2204 path = "/authz/role",
2207 errorCodes = { 404,406 },
2208 text = { "Delete the Role referenced by RoleKey",
2209 "You cannot normally delete a role which still has permissions granted or users assigned to it,",
2210 "however the \"force\" property allows you to do just that. To do this: Add 'force=true'",
2211 "as a query parameter.",
2212 "<p>WARNING: Using force will remove all users and permission from this role. Use with care.</p>"}
2216 public Result<Void> deleteRole(final AuthzTrans trans, REQUEST from) {
2217 final Result<RoleDAO.Data> rd = mapper.role(trans, from);
2218 final ServiceValidator v = new ServiceValidator();
2220 return Result.err(Status.ERR_BadData,"Request does not contain Role");
2222 if (v.nullOrBlank(rd.value).err()) {
2223 return Result.err(Status.ERR_BadData,v.errs());
2225 final RoleDAO.Data role = rd.value;
2226 if (ques.roleDAO().read(trans, role).notOKorIsEmpty() && !trans.requested(force)) {
2227 return Result.err(Status.ERR_RoleNotFound, "Role [" + role.fullName() + "] does not exist");
2230 Result<FutureDAO.Data> fd = mapper.future(trans,RoleDAO.TABLE,from,role,false,
2231 () -> "Delete Role [" + role.fullName() + ']'
2232 + " and all attached user roles",
2234 private Result<NsDAO.Data> nsd;
2236 public Result<?> mayChange() {
2238 nsd = ques.mayUser(trans, trans.user(), role, Access.write);
2246 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rd.value.ns);
2247 if (nsr.notOKorIsEmpty()) {
2248 return Result.err(nsr);
2251 Result<String> rfc = func.createFuture(trans, fd.value,
2252 role.encode(), trans.user(),nsr.value.get(0),FUTURE_OP.D);
2254 return Result.err(Status.ACC_Future, "Role Deletion [%s.%s] is saved for future processing",
2258 return Result.err(rfc);
2260 case Status.ACC_Now:
2261 return func.deleteRole(trans,role,trans.requested(force), true /*preapproved*/);
2263 return Result.err(fd);
2268 /***********************************
2270 ***********************************/
2271 private class MayCreateCred implements MayChange {
2272 private Result<NsDAO.Data> nsd;
2273 private AuthzTrans trans;
2274 private CredDAO.Data cred;
2275 private Executor exec;
2277 public MayCreateCred(AuthzTrans trans, CredDAO.Data cred, Executor exec) {
2284 public Result<?> mayChange() {
2286 nsd = ques.validNSOfDomain(trans, cred.id);
2288 // is Ns of CredID valid?
2292 if (trans.org().validate(trans,Policy.CREATE_MECHID, exec, cred.id)==null) {
2295 Result<?> rmc = ques.mayUser(trans, trans.user(), nsd.value, Access.write);
2296 if (rmc.isOKhasData()) {
2300 } catch (Exception e) {
2301 trans.warn().log(e);
2304 trans.warn().log(nsd.errorString());
2306 return Result.err(Status.ERR_Denied,"%s is not allowed to create %s in %s",trans.user(),cred.id,cred.ns);
2310 private class MayChangeCred implements MayChange {
2311 private static final String EXTEND = "extend";
2312 private static final String RESET = "reset";
2313 private static final String DELETE = "delete";
2314 private Result<NsDAO.Data> nsd;
2315 private AuthzTrans trans;
2316 private CredDAO.Data cred;
2317 private String action;
2318 public MayChangeCred(AuthzTrans trans, CredDAO.Data cred, String action) {
2321 this.action = action;
2325 public Result<?> mayChange() {
2326 // User can change himself (but not create)
2328 nsd = ques.validNSOfDomain(trans, cred.id);
2330 // Get the Namespace
2332 String ns = nsd.value.name;
2333 String user = trans.user();
2335 String temp[] = Split.split('.',ns);
2336 switch(temp.length) {
2338 company = Defaults.AAF_NS;
2344 company = temp[0] + '.' + temp[1];
2348 if(ques.isOwner(trans, user,ns) ||
2349 ques.isAdmin(trans, user,ns) ||
2350 ques.isGranted(trans, user, ROOT_NS,"password",company,DELETE)) {
2356 if (ques.isGranted(trans, trans.user(), ROOT_NS,"password",company,action)) {
2362 return Result.err(Status.ERR_Denied,"%s is not allowed to %s %s in %s",trans.user(),action,cred.id,cred.ns);
2366 private final long DAY_IN_MILLIS = 24*3600*1000L;
2370 path = "/authn/cred",
2373 errorCodes = {403,404,406,409},
2374 text = { "A credential consists of:",
2375 "<ul><li>id - the ID to create within AAF. The domain is in reverse",
2376 "order of Namespace (i.e. Users of Namespace com.att.myapp would be",
2377 "AB1234@myapp.att.com</li>",
2378 "<li>password - Company Policy Compliant Password</li></ul>",
2379 "Note: AAF does support multiple credentials with the same ID.",
2380 "Check with your organization if you have this implemented."
2384 public Result<Void> createUserCred(final AuthzTrans trans, REQUEST from) {
2385 final String cmdDescription = ("Create User Credential");
2386 TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2389 Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2390 if (rcred.isOKhasData()) {
2391 rcred = ques.userCredSetup(trans, rcred.value);
2393 final ServiceValidator v = new ServiceValidator();
2395 if (v.cred(trans, trans.org(),rcred,true).err()) { // Note: Creates have stricter Validations
2396 return Result.err(Status.ERR_BadData,v.errs());
2400 // 2016-4 Jonathan, New Behavior - If MechID is not registered with Org, deny creation
2401 Identity mechID = null;
2402 Organization org = trans.org();
2404 mechID = org.getIdentity(trans, rcred.value.id);
2405 } catch (Exception e1) {
2406 trans.error().log(e1,rcred.value.id,"cannot be validated at this time");
2408 if (mechID==null || !mechID.isFound()) {
2409 return Result.err(Status.ERR_Policy,"MechIDs must be registered with %s before provisioning in AAF",org.getName());
2412 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rcred.value.ns);
2413 if (nsr.notOKorIsEmpty()) {
2414 return Result.err(Status.ERR_NsNotFound,"Cannot provision %s on non-existent Namespace %s",mechID.id(),rcred.value.ns);
2418 boolean firstID = false;
2421 CassExecutor exec = new CassExecutor(trans, func);
2422 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, rcred.value.id);
2423 if (rlcd.isOKhasData()) {
2424 if (!org.canHaveMultipleCreds(rcred.value.id)) {
2425 return Result.err(Status.ERR_ConflictAlreadyExists, "Credential exists");
2428 for (CredDAO.Data curr : rlcd.value) {
2429 // May not use the same password in the list
2430 // Note: ASPR specifies character differences, but we don't actually store the
2431 // password to validate char differences.
2433 // byte[] rawCred = rcred.value.type==CredDAO.RAW?null:; return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2434 if(rcred.value.type==CredDAO.FQI ) {
2435 if(curr.type==CredDAO.FQI) {
2436 return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2440 rb = ques.userCredCheck(trans, curr, rcred.value.cred!=null?rcred.value.cred.array():null);
2442 return Result.err(rb);
2443 } else if (rb.value){
2444 return Result.err(Status.ERR_Policy, "Credential content cannot be reused.");
2445 } else if ((Chrono.dateOnlyStamp(curr.expires).equals(Chrono.dateOnlyStamp(rcred.value.expires)) && curr.type==rcred.value.type)) {
2446 return Result.err(Status.ERR_ConflictAlreadyExists, "Credential with same Expiration Date exists");
2452 // 2016-04-12 Jonathan If Caller is the Sponsor and is also an Owner of NS, allow without special Perm
2453 String theMechID = rcred.value.id;
2454 Boolean otherMechIDs = false;
2455 // find out if this is the only mechID. other MechIDs mean special handling (not automated)
2456 for (CredDAO.Data cd : ques.credDAO().readNS(trans,nsr.value.get(0).name).value) {
2457 if (!cd.id.equals(theMechID)) {
2458 otherMechIDs = true;
2463 // We can say "ID does not exist" here
2464 if ((reason=org.validate(trans, Policy.CREATE_MECHID, exec, theMechID,trans.user(),otherMechIDs.toString()))!=null) {
2465 return Result.err(Status.ERR_Denied, reason);
2468 } catch (Exception e) {
2469 return Result.err(e);
2473 mc = new MayCreateCred(trans, rcred.value, exec);
2475 final CredDAO.Data cdd = rcred.value;
2476 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false, // may want to enable in future.
2479 public String get() {
2480 return cmdDescription + " [" +
2483 + cdd.expires + ']';
2490 Result<String> rfc = func.createFuture(trans, fd.value,
2491 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2492 trans.user(), nsr.value.get(0), FUTURE_OP.C);
2494 return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s] is saved for future processing",
2496 Integer.toString(rcred.value.type),
2497 rcred.value.expires.toString());
2499 return Result.err(rfc);
2501 case Status.ACC_Now:
2504 // && !nsr.value.get(0).isAdmin(trans.getUserPrincipal().getName())) {
2505 Result<List<String>> admins = func.getAdmins(trans, nsr.value.get(0).name, false);
2506 // OK, it's a first ID, and not by NS Admin, so let's set TempPassword length
2507 // Note, we only do this on First time, because of possibility of
2508 // prematurely expiring a production id
2509 if (admins.isOKhasData() && !admins.value.contains(trans.user())) {
2510 rcred.value.expires = org.expiration(null, Expiration.TempPassword).getTime();
2513 } catch (Exception e) {
2514 trans.error().log(e, "While setting expiration to TempPassword");
2517 Result<?>udr = ques.credDAO().create(trans, rcred.value);
2521 return Result.err(udr);
2523 return Result.err(fd);
2527 return Result.err(rcred);
2536 path = "/authn/creds/ns/:ns",
2537 params = {"ns|string|true"},
2539 errorCodes = {403,404,406},
2540 text = { "Return all IDs in Namespace :ns"
2544 public Result<USERS> getCredsByNS(AuthzTrans trans, String ns) {
2545 final Validator v = new ServiceValidator();
2546 if (v.ns(ns).err()) {
2547 return Result.err(Status.ERR_BadData,v.errs());
2550 // check if user is allowed to view NS
2551 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2553 return Result.err(rnd);
2555 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2557 return Result.err(rnd);
2560 TimeTaken tt = trans.start("MAP Creds by NS to Creds", Env.SUB);
2562 USERS users = mapper.newInstance(API.USERS);
2563 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readNS(trans, ns);
2566 if (!rlcd.isEmpty()) {
2567 return mapper.cred(rlcd.value, users);
2569 return Result.ok(users);
2571 return Result.err(rlcd);
2581 path = "/authn/creds/id/:ns",
2582 params = {"id|string|true"},
2584 errorCodes = {403,404,406},
2585 text = { "Return all IDs in for ID"
2586 ,"(because IDs are multiple, due to multiple Expiration Dates)"
2590 public Result<USERS> getCredsByID(AuthzTrans trans, String id) {
2591 final Validator v = new ServiceValidator();
2592 if (v.nullOrBlank("ID",id).err()) {
2593 return Result.err(Status.ERR_BadData,v.errs());
2596 String ns = Question.domain2ns(id);
2597 // check if user is allowed to view NS
2598 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
2600 return Result.err(rnd);
2602 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
2604 return Result.err(rnd);
2607 TimeTaken tt = trans.start("MAP Creds by ID to Creds", Env.SUB);
2609 USERS users = mapper.newInstance(API.USERS);
2610 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, id);
2613 if (!rlcd.isEmpty()) {
2614 return mapper.cred(rlcd.value, users);
2616 return Result.ok(users);
2618 return Result.err(rlcd);
2628 path = "/authn/certs/id/:id",
2629 params = {"id|string|true"},
2631 errorCodes = {403,404,406},
2632 text = { "Return Cert Info for ID"
2636 public Result<CERTS> getCertInfoByID(AuthzTrans trans, HttpServletRequest req, String id) {
2637 TimeTaken tt = trans.start("Get Cert Info by ID", Env.SUB);
2639 CERTS certs = mapper.newInstance(API.CERTS);
2640 Result<List<CertDAO.Data>> rlcd = ques.certDAO().readID(trans, id);
2643 if (!rlcd.isEmpty()) {
2644 return mapper.cert(rlcd.value, certs);
2646 return Result.ok(certs);
2648 return Result.err(rlcd);
2658 path = "/authn/cred",
2661 errorCodes = {300,403,404,406},
2662 text = { "Reset a Credential Password. If multiple credentials exist for this",
2663 "ID, you will need to specify which entry you are resetting in the",
2664 "CredRequest object"
2668 public Result<Void> resetUserCred(final AuthzTrans trans, REQUEST from) {
2669 final String cmdDescription = "Update User Credential";
2670 TimeTaken tt = trans.start(cmdDescription, Env.SUB);
2672 Result<CredDAO.Data> rcred = mapper.cred(trans, from, true);
2673 if (rcred.isOKhasData()) {
2674 rcred = ques.userCredSetup(trans, rcred.value);
2676 final ServiceValidator v = new ServiceValidator();
2678 if (v.cred(trans, trans.org(),rcred,false).err()) {// Note: Creates have stricter Validations
2679 return Result.err(Status.ERR_BadData,v.errs());
2681 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, rcred.value.id);
2682 if (rlcd.notOKorIsEmpty()) {
2683 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2686 MayChange mc = new MayChangeCred(trans, rcred.value,MayChangeCred.RESET);
2687 Result<?> rmc = mc.mayChange();
2689 return Result.err(rmc);
2692 List<CredDAO.Data> lcdd = filterList(rlcd.value,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
2694 Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, lcdd, MayChangeCred.RESET);
2696 return Result.err(ri);
2698 int entry = ri.value;
2701 final CredDAO.Data cred = rcred.value;
2703 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from, rcred.value,false,
2706 public String get() {
2707 return cmdDescription + " [" +
2710 + cred.expires + ']';
2715 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, rcred.value.ns);
2716 if (nsr.notOKorIsEmpty()) {
2717 return Result.err(nsr);
2722 Result<String> rfc = func.createFuture(trans, fd.value,
2723 rcred.value.id + '|' + rcred.value.type.toString() + '|' + rcred.value.expires,
2724 trans.user(), nsr.value.get(0), FUTURE_OP.U);
2726 return Result.err(Status.ACC_Future, "Credential Request [%s|%s|%s]",
2728 Integer.toString(rcred.value.type),
2729 rcred.value.expires.toString());
2731 return Result.err(rfc);
2733 case Status.ACC_Now:
2734 Result<?>udr = null;
2735 // If we are Resetting Password on behalf of someone else (am not the Admin)
2736 // use TempPassword Expiration time.
2738 if (ques.isAdmin(trans, trans.user(), nsr.value.get(0).name)) {
2739 exp = Expiration.Password;
2741 exp = Expiration.TempPassword;
2744 Organization org = trans.org();
2745 CredDAO.Data current = rlcd.value.get(entry);
2746 // If user resets password in same day, we will have a primary key conflict, so subtract 1 day
2747 if (current.expires.equals(rcred.value.expires)
2748 && rlcd.value.get(entry).type==rcred.value.type) {
2749 GregorianCalendar gc = org.expiration(null, exp,rcred.value.id);
2750 gc = Chrono.firstMomentOfDay(gc);
2751 gc.set(GregorianCalendar.HOUR_OF_DAY, org.startOfDay());
2752 rcred.value.expires = new Date(gc.getTimeInMillis() - DAY_IN_MILLIS);
2754 rcred.value.expires = org.expiration(null,exp).getTime();
2757 udr = ques.credDAO().create(trans, rcred.value);
2759 udr = ques.credDAO().delete(trans, rlcd.value.get(entry),false);
2765 return Result.err(udr);
2767 return Result.err(fd);
2770 return Result.err(rcred);
2779 path = "/authn/cred/:days",
2780 params = {"days|string|true"},
2782 errorCodes = {300,403,404,406},
2783 text = { "Extend a Credential Expiration Date. The intention of this API is",
2784 "to avoid an outage in PROD due to a Credential expiring before it",
2785 "can be configured correctly. Measures are being put in place ",
2786 "so that this is not abused."
2790 public Result<Void> extendUserCred(final AuthzTrans trans, REQUEST from, String days) {
2791 TimeTaken tt = trans.start("Extend User Credential", Env.SUB);
2793 Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2794 Organization org = trans.org();
2795 final ServiceValidator v = new ServiceValidator();
2796 if (v.notOK(cred).err() ||
2797 v.nullOrBlank(cred.value.id, "Invalid ID").err() ||
2798 v.user(org,cred.value.id).err()) {
2799 return Result.err(Status.ERR_BadData,v.errs());
2804 if ((reason=org.validate(trans, Policy.MAY_EXTEND_CRED_EXPIRES, new CassExecutor(trans,func)))!=null) {
2805 return Result.err(Status.ERR_Policy,reason);
2807 } catch (Exception e) {
2809 trans.error().log(e, msg="Could not contact Organization for User Validation");
2810 return Result.err(Status.ERR_Denied, msg);
2813 // Get the list of Cred Entries
2814 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, cred.value.id);
2815 if (rlcd.notOKorIsEmpty()) {
2816 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2819 // Only Passwords can be extended
2820 List<CredDAO.Data> lcdd = filterList(rlcd.value,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
2822 //Need to do the "Pick Entry" mechanism
2824 Result<Integer> ri = selectEntryIfMultiple((CredRequest)from, lcdd, MayChangeCred.EXTEND);
2826 return Result.err(ri);
2829 CredDAO.Data found = lcdd.get(ri.value);
2830 CredDAO.Data cd = cred.value;
2831 // Copy over the cred
2833 cd.cred = found.cred;
2834 cd.other = found.other;
2835 cd.type = found.type;
2837 cd.notes = "Extended";
2839 cd.expires = org.expiration(null, Expiration.ExtendPassword,days).getTime();
2840 if(cd.expires.before(found.expires)) {
2841 return Result.err(Result.ERR_BadData,String.format("Credential's expiration date is more than %s days in the future",days));
2844 cred = ques.credDAO().create(trans, cd);
2848 return Result.err(cred);
2856 path = "/authn/cred",
2859 errorCodes = {300,403,404,406},
2860 text = { "Delete a Credential. If multiple credentials exist for this",
2861 "ID, you will need to specify which entry you are deleting in the",
2862 "CredRequest object."
2866 public Result<Void> deleteUserCred(AuthzTrans trans, REQUEST from) {
2867 final Result<CredDAO.Data> cred = mapper.cred(trans, from, false);
2868 final Validator v = new ServiceValidator();
2869 if (v.nullOrBlank("cred", cred.value.id).err()) {
2870 return Result.err(Status.ERR_BadData,v.errs());
2873 MayChange mc = new MayChangeCred(trans,cred.value,MayChangeCred.DELETE);
2874 Result<?> rmc = mc.mayChange();
2876 return Result.err(rmc);
2879 boolean doForce = trans.requested(force);
2880 Result<List<CredDAO.Data>> rlcd = ques.credDAO().readID(trans, cred.value.id);
2881 if (rlcd.notOKorIsEmpty()) {
2882 // Empty Creds should not have user_roles.
2883 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, cred.value.id);
2884 if (rlurd.isOKhasData()) {
2885 for (UserRoleDAO.Data data : rlurd.value) {
2886 ques.userRoleDAO().delete(trans, data, false);
2889 return Result.err(Status.ERR_UserNotFound, "Credential does not exist");
2891 boolean isLastCred = rlcd.value.size()==1;
2894 CredRequest cr = (CredRequest)from;
2896 if(cr.getEntry()==null || "1".equals(cr.getEntry())) {
2899 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2904 if(cred.value.type==CredDAO.FQI) {
2906 for(CredDAO.Data cdd : rlcd.value) {
2908 if(cdd.type == CredDAO.FQI) {
2915 if (rlcd.value.size() > 1) {
2916 String inputOption = cr.getEntry();
2917 if (inputOption == null) {
2918 List<CredDAO.Data> list = filterList(rlcd.value,CredDAO.BASIC_AUTH,CredDAO.BASIC_AUTH_SHA256,CredDAO.CERT_SHA256_RSA);
2919 String message = selectCredFromList(list, MayChangeCred.DELETE);
2920 Object[] variables = buildVariables(list);
2921 return Result.err(Status.ERR_ChoiceNeeded, message, variables);
2924 if (inputOption.length()>5) { // should be a date
2925 Date d = Chrono.xmlDatatypeFactory.newXMLGregorianCalendar(inputOption).toGregorianCalendar().getTime();
2926 for (CredDAO.Data cd : rlcd.value) {
2928 if (cd.type.equals(cr.getType()) && cd.expires.equals(d)) {
2934 entry = Integer.parseInt(inputOption) - 1;
2936 for (CredDAO.Data cd : rlcd.value) {
2937 if(cd.type!=CredDAO.BASIC_AUTH && cd.type!=CredDAO.BASIC_AUTH_SHA256 && cd.type!=CredDAO.CERT_SHA256_RSA) {
2945 } catch (NullPointerException e) {
2946 return Result.err(Status.ERR_BadData, "Invalid Date Format for Entry");
2947 } catch (NumberFormatException e) {
2948 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2951 isLastCred = (entry==-1)?true:false;
2955 if (entry < -1 || entry >= rlcd.value.size()) {
2956 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
2962 Result<FutureDAO.Data> fd = mapper.future(trans,CredDAO.TABLE,from,cred.value,false,
2963 () -> "Delete Credential [" +
2968 Result<List<NsDAO.Data>> nsr = ques.nsDAO().read(trans, cred.value.ns);
2969 if (nsr.notOKorIsEmpty()) {
2970 return Result.err(nsr);
2975 Result<String> rfc = func.createFuture(trans, fd.value, cred.value.id,
2976 trans.user(), nsr.value.get(0), FUTURE_OP.D);
2979 return Result.err(Status.ACC_Future, "Credential Delete [%s] is saved for future processing",cred.value.id);
2981 return Result.err(rfc);
2983 case Status.ACC_Now:
2984 Result<?>udr = null;
2985 if (!trans.requested(force)) {
2986 if (entry<0 || entry >= rlcd.value.size()) {
2987 if(cred.value.type==CredDAO.FQI) {
2988 return Result.err(Status.ERR_BadData,"FQI does not exist");
2990 return Result.err(Status.ERR_BadData,"Invalid Choice [" + entry + "] chosen for Delete [%s] is saved for future processing",cred.value.id);
2993 udr = ques.credDAO().delete(trans, rlcd.value.get(entry),false);
2995 for (CredDAO.Data curr : rlcd.value) {
2996 udr = ques.credDAO().delete(trans, curr, false);
2998 return Result.err(udr);
3003 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, cred.value.id);
3005 for (UserRoleDAO.Data data : rlurd.value) {
3006 ques.userRoleDAO().delete(trans, data, false);
3011 return Result.err(Result.ERR_NotFound,"No User Data found");
3016 return Result.err(udr);
3018 return Result.err(fd);
3024 * Codify the way to get Either Choice Needed or actual Integer from Credit Request
3026 private Result<Integer> selectEntryIfMultiple(final CredRequest cr, List<CredDAO.Data> lcd, String action) {
3028 if (lcd.size() > 1) {
3029 String inputOption = cr.getEntry();
3030 if (inputOption == null) {
3031 String message = selectCredFromList(lcd, action);
3032 Object[] variables = buildVariables(lcd);
3033 return Result.err(Status.ERR_ChoiceNeeded, message, variables);
3035 if(MayChangeCred.EXTEND.equals(action)) {
3037 if(inputOption.length()>4) { //Tag is at least 12
3039 CredDAO.Data last = null;
3041 for(CredDAO.Data cdd : lcd) {
3042 if(inputOption.equals(cdd.tag)) {
3047 if(last.expires.before(cdd.expires)) {
3056 return Result.ok(lastIdx);
3058 return Result.err(Status.ERR_BadData, "User chose unknown Tag");
3061 entry = Integer.parseInt(inputOption) - 1;
3063 if (entry < 0 || entry >= lcd.size()) {
3064 return Result.err(Status.ERR_BadData, "User chose invalid credential selection");
3067 return Result.ok(entry);
3070 private List<CredDAO.Data> filterList(List<CredDAO.Data> orig, Integer ... types) {
3071 List<CredDAO.Data> rv = new ArrayList<>();
3072 for(CredDAO.Data cdd : orig) {
3074 for(int t : types) {
3081 Collections.sort(rv, (o1,o2) -> {
3082 if(o1.type==o2.type) {
3083 return o1.expires.compareTo(o2.expires);
3085 return o1.type.compareTo(o2.type);
3091 private String[] buildVariables(List<CredDAO.Data> value) {
3092 String [] vars = new String[value.size()];
3095 for (int i = 0; i < value.size(); i++) {
3097 vars[i] = cdd.id + TWO_SPACE + Define.getCredType(cdd.type) + TWO_SPACE + Chrono.niceUTCStamp(cdd.expires) + TWO_SPACE + cdd.tag;
3102 private String selectCredFromList(List<CredDAO.Data> value, String action) {
3103 StringBuilder errMessage = new StringBuilder();
3104 String userPrompt = MayChangeCred.DELETE.equals(action)?
3105 "Select which cred to delete (set force=true to delete all):":
3106 "Select which cred to " + action + ':';
3107 int numSpaces = value.get(0).id.length() - "Id".length();
3109 errMessage.append(userPrompt + '\n');
3110 errMessage.append(" ID");
3111 for (int i = 0; i < numSpaces; i++) {
3112 errMessage.append(' ');
3114 errMessage.append(" Type Expires Tag " + '\n');
3115 for (int i=0;i<value.size();++i) {
3116 errMessage.append(" %s\n");
3118 if(MayChangeCred.EXTEND.equals(action)) {
3119 errMessage.append("Run same command again with chosen entry or Tag as last parameter");
3121 errMessage.append("Run same command again with chosen entry as last parameter");
3123 return errMessage.toString();
3128 public Result<Date> doesCredentialMatch(AuthzTrans trans, REQUEST credReq) {
3129 TimeTaken tt = trans.start("Does Credential Match", Env.SUB);
3131 // Note: Mapper assigns RAW type
3132 Result<CredDAO.Data> data = mapper.cred(trans, credReq,false);
3133 if (data.notOKorIsEmpty()) {
3134 return Result.err(data);
3136 CredDAO.Data cred = data.value; // of the Mapped Cred
3137 if (cred.cred==null) {
3138 return Result.err(Result.ERR_BadData,"No Password");
3140 return ques.doesUserCredMatch(trans, cred.id, cred.cred.array());
3143 } catch (DAOException e) {
3144 trans.error().log(e,"Error looking up cred");
3145 return Result.err(Status.ERR_Denied,"Credential does not match");
3153 path = "/authn/validate",
3156 errorCodes = { 403 },
3157 text = { "Validate a Credential given a Credential Structure. This is a more comprehensive validation, can "
3158 + "do more than BasicAuth as Credential types exp" }
3161 public Result<Date> validateBasicAuth(AuthzTrans trans, String basicAuth) {
3162 //TODO how to make sure people don't use this in browsers? Do we care?
3163 TimeTaken tt = trans.start("Validate Basic Auth", Env.SUB);
3165 BasicPrincipal bp = new BasicPrincipal(basicAuth,trans.org().getRealm());
3166 Result<Date> rq = ques.doesUserCredMatch(trans, bp.getName(), bp.getCred());
3167 // Note: Only want to log problem, don't want to send back to end user
3171 trans.audit().log(rq.errorString());
3173 } catch (Exception e) {
3174 trans.warn().log(e);
3178 return Result.err(Status.ERR_Denied,"Bad Basic Auth");
3183 path = "/authn/basicAuth",
3186 errorCodes = { 403 },
3187 text = { "!!!! DEPRECATED without X509 Authentication STOP USING THIS API BY DECEMBER 2017, or use Certificates !!!!\n"
3188 + "Use /authn/validate instead\n"
3189 + "Note: Validate a Password using BasicAuth Base64 encoded Header. This HTTP/S call is intended as a fast"
3190 + " User/Password lookup for Security Frameworks, and responds 200 if it passes BasicAuth "
3191 + "security, and 403 if it does not." }
3193 private void basicAuth() {
3194 // This is a place holder for Documentation. The real BasicAuth API does not call Service.
3197 /***********************************
3199 ***********************************/
3202 path = "/authz/userRole",
3205 errorCodes = {403,404,406,409},
3206 text = { "Create a UserRole relationship (add User to Role)",
3207 "A UserRole is an object Representation of membership of a Role for limited time.",
3208 "If a shorter amount of time for Role ownership is required, use the 'End' field.",
3209 "** Note: Owners of Namespaces will be required to revalidate users in these roles ",
3210 "before Expirations expire. Namespace owners will be notified by email."
3214 public Result<Void> createUserRole(final AuthzTrans trans, REQUEST from) {
3215 TimeTaken tt = trans.start("Create UserRole", Env.SUB);
3217 Result<UserRoleDAO.Data> urr = mapper.userRole(trans, from);
3218 if (urr.notOKorIsEmpty()) {
3219 return Result.err(urr);
3221 final UserRoleDAO.Data userRole = urr.value;
3223 final ServiceValidator v = new ServiceValidator();
3224 if (v.user_role(trans.user(),userRole).err() ||
3225 v.user(trans.org(), userRole.user).err()) {
3226 return Result.err(Status.ERR_BadData,v.errs());
3231 // Check if user can change first
3232 Result<FutureDAO.Data> fd = mapper.future(trans,UserRoleDAO.TABLE,from,urr.value,true, // may request Approvals
3233 () -> "Add User [" + userRole.user + "] to Role [" +
3237 private Result<NsDAO.Data> nsd;
3239 public Result<?> mayChange() {
3240 if(urr.value.role.startsWith(urr.value.user)) {
3241 return Result.ok((NsDAO.Data)null);
3244 RoleDAO.Data r = RoleDAO.Data.decode(userRole);
3245 nsd = ques.mayUser(trans, trans.user(), r, Access.write);
3252 if(userRole.role.startsWith(userRole.user)) {
3253 userRole.ns=userRole.user;
3254 userRole.rname="user";
3257 Result<NsDAO.Data> nsr = ques.deriveNs(trans, userRole.role);
3259 return Result.err(nsr);
3266 Result<String> rfc = func.createFuture(trans, fd.value, userRole.user+'|'+userRole.ns + '.' + userRole.rname,
3267 userRole.user, ndd, FUTURE_OP.C);
3269 return Result.err(Status.ACC_Future, "UserRole [%s - %s.%s] is saved for future processing",
3274 return Result.err(rfc);
3276 case Status.ACC_Now:
3277 return func.addUserRole(trans, userRole);
3279 return Result.err(fd);
3287 * getUserRolesByRole
3291 path = "/authz/userRoles/role/:role",
3292 params = {"role|string|true"},
3294 errorCodes = {404,406},
3295 text = { "List all Users that are attached to Role specified in :role",
3299 public Result<USERROLES> getUserRolesByRole(AuthzTrans trans, String role) {
3300 final Validator v = new ServiceValidator();
3301 if (v.nullOrBlank("Role",role).err()) {
3302 return Result.err(Status.ERR_BadData,v.errs());
3305 Result<RoleDAO.Data> rrdd;
3306 rrdd = RoleDAO.Data.decode(trans,ques,role);
3308 return Result.err(rrdd);
3310 // May Requester see result?
3311 Result<NsDAO.Data> ns = ques.mayUser(trans,trans.user(), rrdd.value,Access.read);
3313 return Result.err(ns);
3316 // boolean filter = true;
3317 // if (ns.value.isAdmin(trans.user()) || ns.value.isResponsible(trans.user()))
3320 // Get list of roles per user, then add to Roles as we go
3321 HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3322 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role);
3324 for (UserRoleDAO.Data data : rlurd.value) {
3329 @SuppressWarnings("unchecked")
3330 USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3331 // Checked for permission
3332 mapper.userRoles(trans, userSet, users);
3333 return Result.ok(users);
3336 * getUserRolesByRole
3340 path = "/authz/userRoles/user/:user",
3341 params = {"role|string|true"},
3343 errorCodes = {404,406},
3344 text = { "List all UserRoles for :user",
3348 public Result<USERROLES> getUserRolesByUser(AuthzTrans trans, String user) {
3349 final Validator v = new ServiceValidator();
3350 if (v.nullOrBlank("User",user).err()) {
3351 return Result.err(Status.ERR_BadData,v.errs());
3354 // Get list of roles per user, then add to Roles as we go
3355 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByUser(trans, user);
3356 if (rlurd.notOK()) {
3357 return Result.err(rlurd);
3362 * 2) is User's Supervisor
3363 * 3) Has special global access =read permission
3365 * If none of the 3, then filter results to NSs in which Calling User has Ns.access * read
3368 String callingUser = trans.getUserPrincipal().getName();
3369 NsDAO.Data ndd = new NsDAO.Data();
3371 if (user.equals(callingUser)) {
3374 Organization org = trans.org();
3376 Identity orgID = org.getIdentity(trans, user);
3377 Identity manager = orgID==null?null:orgID.responsibleTo();
3378 if (orgID!=null && (manager!=null && callingUser.equals(manager.fullID()))) {
3380 } else if (ques.isGranted(trans, callingUser, ROOT_NS, Question.ACCESS, "*", Access.read.name())) {
3385 } catch (OrganizationException e) {
3391 List<UserRoleDAO.Data> content;
3393 content = new ArrayList<>(rlurd.value.size()); // avoid multi-memory redos
3395 for (UserRoleDAO.Data data : rlurd.value) {
3397 Result<Data> mur = ques.mayUser(trans, callingUser, ndd, Access.read);
3404 content = rlurd.value;
3408 @SuppressWarnings("unchecked")
3409 USERROLES users = (USERROLES) mapper.newInstance(API.USER_ROLES);
3410 // Checked for permission
3411 mapper.userRoles(trans, content, users);
3412 return Result.ok(users);
3420 path = "/authz/userRole/extend/:user/:role",
3421 params = { "user|string|true",
3425 errorCodes = {403,404,406},
3426 text = { "Extend the Expiration of this User Role by the amount set by Organization",
3427 "Requestor must be allowed to modify the role"
3431 public Result<Void> extendUserRole(AuthzTrans trans, String user, String role) {
3432 Organization org = trans.org();
3433 final ServiceValidator v = new ServiceValidator();
3434 if (v.user(org, user)
3437 return Result.err(Status.ERR_BadData,v.errs());
3440 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3442 return Result.err(rrdd);
3445 Result<NsDAO.Data> rcr = ques.mayUser(trans, trans.user(), rrdd.value, Access.write);
3446 boolean mayNotChange;
3447 if ((mayNotChange = rcr.notOK()) && !trans.requested(future)) {
3448 return Result.err(rcr);
3451 Result<List<UserRoleDAO.Data>> rr = ques.userRoleDAO().read(trans, user,role);
3453 return Result.err(rr);
3455 for (UserRoleDAO.Data userRole : rr.value) {
3456 if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3457 FutureDAO.Data fto = new FutureDAO.Data();
3458 fto.target=UserRoleDAO.TABLE;
3459 fto.memo = "Extend User ["+userRole.user+"] in Role ["+userRole.role+"]";
3460 GregorianCalendar now = new GregorianCalendar();
3461 fto.start = now.getTime();
3462 fto.expires = org.expiration(now, Expiration.Future).getTime();
3464 fto.construct = userRole.bytify();
3465 } catch (IOException e) {
3466 trans.error().log(e, "Error while bytifying UserRole for Future");
3467 return Result.err(e);
3470 Result<String> rfc = func.createFuture(trans, fto,
3471 userRole.user+'|'+userRole.role, userRole.user, rcr.value, FUTURE_OP.U);
3473 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3477 return Result.err(rfc);
3480 return func.extendUserRole(trans, userRole, false);
3483 return Result.err(Result.ERR_NotFound,"This user and role doesn't exist");
3488 path = "/authz/userRole/:user/:role",
3489 params = { "user|string|true",
3493 errorCodes = {403,404,406},
3494 text = { "Remove Role :role from User :user."
3498 public Result<Void> deleteUserRole(AuthzTrans trans, String usr, String role) {
3499 Validator val = new ServiceValidator();
3500 if (val.nullOrBlank("User", usr)
3501 .nullOrBlank("Role", role).err()) {
3502 return Result.err(Status.ERR_BadData, val.errs());
3505 boolean mayNotChange;
3506 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans,ques,role);
3508 return Result.err(rrdd);
3511 RoleDAO.Data rdd = rrdd.value;
3512 Result<NsDAO.Data> rns = ques.mayUser(trans, trans.user(), rdd, Access.write);
3514 // Make sure we don't delete the last owner of valid NS
3515 if (rns.isOKhasData() && Question.OWNER.equals(rdd.name) && ques.countOwner(trans,rdd.ns)<=1) {
3516 return Result.err(Status.ERR_Denied,"You may not delete the last Owner of " + rdd.ns );
3519 if (mayNotChange=rns.notOK()) {
3520 if (!trans.requested(future)) {
3521 return Result.err(rns);
3525 Result<List<UserRoleDAO.Data>> rulr;
3526 if ((rulr=ques.userRoleDAO().read(trans, usr, role)).notOKorIsEmpty()) {
3527 return Result.err(Status.ERR_UserRoleNotFound, "User [ "+usr+" ] is not "
3528 + "Assigned to the Role [ " + role + " ]");
3531 UserRoleDAO.Data userRole = rulr.value.get(0);
3532 if (mayNotChange) { // Function exited earlier if !trans.futureRequested
3533 FutureDAO.Data fto = new FutureDAO.Data();
3534 fto.target=UserRoleDAO.TABLE;
3535 fto.memo = "Remove User ["+userRole.user+"] from Role ["+userRole.role+"]";
3536 GregorianCalendar now = new GregorianCalendar();
3537 fto.start = now.getTime();
3538 fto.expires = trans.org().expiration(now, Expiration.Future).getTime();
3540 Result<String> rfc = func.createFuture(trans, fto,
3541 userRole.user+'|'+userRole.role, userRole.user, rns.value, FUTURE_OP.D);
3543 return Result.err(Status.ACC_Future, "UserRole [%s - %s] is saved for future processing",
3547 return Result.err(rfc);
3550 return ques.userRoleDAO().delete(trans, rulr.value.get(0), false);
3556 path = "/authz/userRole/:user/:role",
3557 params = {"user|string|true",
3558 "role|string|true"},
3560 errorCodes = {403,404,406},
3561 text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3565 public Result<USERS> getUserInRole(AuthzTrans trans, String user, String role) {
3566 final Validator v = new ServiceValidator();
3567 if (v.role(role).nullOrBlank("User", user).err()) {
3568 return Result.err(Status.ERR_BadData,v.errs());
3571 // Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3572 // if (ns.notOK()) return Result.err(ns);
3574 // Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3575 // May calling user see by virtue of the Role
3576 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3578 return Result.err(rrdd);
3580 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3582 return Result.err(rnd);
3585 HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3586 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readUserInRole(trans, user, role);
3588 for (UserRoleDAO.Data data : rlurd.value) {
3593 @SuppressWarnings("unchecked")
3594 USERS users = (USERS) mapper.newInstance(API.USERS);
3595 mapper.users(trans, userSet, users);
3596 return Result.ok(users);
3601 path = "/authz/users/role/:role",
3602 params = {"user|string|true",
3603 "role|string|true"},
3605 errorCodes = {403,404,406},
3606 text = { "Returns the User (with Expiration date from listed User/Role) if it exists"
3610 public Result<USERS> getUsersByRole(AuthzTrans trans, String role) {
3611 final Validator v = new ServiceValidator();
3612 if (v.nullOrBlank("Role",role).err()) {
3613 return Result.err(Status.ERR_BadData,v.errs());
3616 // Result<NsDAO.Data> ns = ques.deriveNs(trans, role);
3617 // if (ns.notOK()) return Result.err(ns);
3619 // Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), ns.value, Access.write);
3620 // May calling user see by virtue of the Role
3621 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3623 return Result.err(rrdd);
3626 boolean contactOnly = false;
3627 // Allow the request of any valid user to find the contact of the NS (Owner)
3628 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value,Access.read);
3630 if (Question.OWNER.equals(rrdd.value.name)) {
3633 return Result.err(rnd);
3637 HashSet<UserRoleDAO.Data> userSet = new HashSet<>();
3638 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role);
3640 for (UserRoleDAO.Data data : rlurd.value) {
3641 if (contactOnly) { //scrub data
3642 // Can't change actual object, or will mess up the cache.
3643 UserRoleDAO.Data scrub = new UserRoleDAO.Data();
3645 scrub.rname = data.rname;
3646 scrub.role = data.role;
3647 scrub.user = data.user;
3655 @SuppressWarnings("unchecked")
3656 USERS users = (USERS) mapper.newInstance(API.USERS);
3657 mapper.users(trans, userSet, users);
3658 return Result.ok(users);
3662 * getUsersByPermission
3666 path = "/authz/users/perm/:type/:instance/:action",
3667 params = { "type|string|true",
3668 "instance|string|true",
3669 "action|string|true"
3672 errorCodes = {404,406},
3673 text = { "List all Users that have Permission specified by :type :instance :action",
3677 public Result<USERS> getUsersByPermission(AuthzTrans trans, String type, String instance, String action) {
3678 final Validator v = new ServiceValidator();
3679 if (v.nullOrBlank("Type",type)
3680 .nullOrBlank("Instance",instance)
3681 .nullOrBlank("Action",action)
3683 return Result.err(Status.ERR_BadData,v.errs());
3686 Result<NsSplit> nss = ques.deriveNsSplit(trans, type);
3688 return Result.err(nss);
3691 Result<List<NsDAO.Data>> nsd = ques.nsDAO().read(trans, nss.value.ns);
3693 return Result.err(nsd);
3696 boolean allInstance = ASTERIX.equals(instance);
3697 boolean allAction = ASTERIX.equals(action);
3698 // Get list of roles per Permission,
3699 // Then loop through Roles to get Users
3700 // Note: Use Sets to avoid processing or responding with Duplicates
3701 Set<String> roleUsed = new HashSet<>();
3702 Set<UserRoleDAO.Data> userSet = new HashSet<>();
3704 if (!nss.isEmpty()) {
3705 Result<List<PermDAO.Data>> rlp = ques.permDAO().readByType(trans, nss.value.ns, nss.value.name);
3706 if (rlp.isOKhasData()) {
3707 for (PermDAO.Data pd : rlp.value) {
3708 if ((allInstance || pd.instance.equals(instance)) &&
3709 (allAction || pd.action.equals(action))) {
3710 if (ques.mayUser(trans, trans.user(),pd,Access.read).isOK()) {
3711 for (String role : pd.roles) {
3712 if (!roleUsed.contains(role)) { // avoid evaluating Role many times
3714 Result<List<UserRoleDAO.Data>> rlurd = ques.userRoleDAO().readByRole(trans, role.replace('|', '.'));
3715 if (rlurd.isOKhasData()) {
3716 for (UserRoleDAO.Data urd : rlurd.value) {
3727 @SuppressWarnings("unchecked")
3728 USERS users = (USERS) mapper.newInstance(API.USERS);
3729 mapper.users(trans, userSet, users);
3730 return Result.ok(users);
3733 /***********************************
3735 ***********************************/
3737 public Result<HISTORY> getHistoryByUser(final AuthzTrans trans, String user, final int[] yyyymm, final int sort) {
3738 final Validator v = new ServiceValidator();
3739 if (v.nullOrBlank("User",user).err()) {
3740 return Result.err(Status.ERR_BadData,v.errs());
3743 Result<NsDAO.Data> rnd;
3744 // Users may look at their own data
3745 if (trans.user().equals(user)) {
3746 // Users may look at their own data
3748 int at = user.indexOf('@');
3749 if (at>=0 && trans.org().getRealm().equals(user.substring(at+1))) {
3750 NsDAO.Data nsd = new NsDAO.Data();
3751 nsd.name = Question.domain2ns(user);
3752 rnd = ques.mayUser(trans, trans.user(), nsd, Access.read);
3754 return Result.err(rnd);
3757 rnd = ques.validNSOfDomain(trans, user);
3759 return Result.err(rnd);
3762 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3764 return Result.err(rnd);
3768 Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readByUser(trans, user, yyyymm);
3770 return Result.err(resp);
3772 return mapper.history(trans, resp.value,sort);
3776 public Result<HISTORY> getHistoryByRole(AuthzTrans trans, String role, int[] yyyymm, final int sort) {
3777 final Validator v = new ServiceValidator();
3778 if (v.nullOrBlank("Role",role).err()) {
3779 return Result.err(Status.ERR_BadData,v.errs());
3782 Result<RoleDAO.Data> rrdd = RoleDAO.Data.decode(trans, ques, role);
3784 return Result.err(rrdd);
3787 Result<NsDAO.Data> rnd = ques.mayUser(trans, trans.user(), rrdd.value, Access.read);
3789 return Result.err(rnd);
3791 Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, role, "role", yyyymm);
3793 return Result.err(resp);
3795 return mapper.history(trans, resp.value,sort);
3799 public Result<HISTORY> getHistoryByPerm(AuthzTrans trans, String type, int[] yyyymm, final int sort) {
3800 final Validator v = new ServiceValidator();
3801 if (v.nullOrBlank("Type",type)
3803 return Result.err(Status.ERR_BadData,v.errs());
3806 // May user see Namespace of Permission (since it's only one piece... we can't check for "is permission part of")
3807 Result<List<HistoryDAO.Data>> resp;
3808 if(type.startsWith(trans.user())) {
3809 resp = ques.historyDAO().readBySubject(trans, type, "perm", yyyymm);
3811 Result<NsDAO.Data> rnd = ques.deriveNs(trans,type);
3813 return Result.err(rnd);
3815 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3817 return Result.err(rnd);
3819 resp = ques.historyDAO().readBySubject(trans, type, "perm", yyyymm);
3823 return Result.err(resp);
3825 return mapper.history(trans, resp.value,sort);
3829 public Result<HISTORY> getHistoryByNS(AuthzTrans trans, String ns, int[] yyyymm, final int sort) {
3830 final Validator v = new ServiceValidator();
3831 if (v.nullOrBlank("NS",ns).err()) {
3832 return Result.err(Status.ERR_BadData,v.errs());
3835 Result<NsDAO.Data> rnd = ques.deriveNs(trans,ns);
3837 return Result.err(rnd);
3839 rnd = ques.mayUser(trans, trans.user(), rnd.value, Access.read);
3841 return Result.err(rnd);
3844 Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, ns, "ns", yyyymm);
3846 return Result.err(resp);
3848 return mapper.history(trans, resp.value,sort);
3852 public Result<HISTORY> getHistoryBySubject(AuthzTrans trans, String subject, String target, int[] yyyymm, final int sort) {
3853 NsDAO.Data ndd = new NsDAO.Data();
3854 ndd.name = FQI.reverseDomain(subject);
3855 Result<Data> rnd = ques.mayUser(trans, trans.user(), ndd, Access.read);
3857 return Result.err(rnd);
3860 Result<List<HistoryDAO.Data>> resp = ques.historyDAO().readBySubject(trans, subject, target, yyyymm);
3862 return Result.err(resp);
3864 return mapper.history(trans, resp.value,sort);
3867 /***********************************
3869 ***********************************/
3871 public Result<Void> createDelegate(final AuthzTrans trans, REQUEST base) {
3872 return createOrUpdateDelegate(trans, base, Question.Access.create);
3876 public Result<Void> updateDelegate(AuthzTrans trans, REQUEST base) {
3877 return createOrUpdateDelegate(trans, base, Question.Access.write);
3881 private Result<Void> createOrUpdateDelegate(final AuthzTrans trans, REQUEST base, final Access access) {
3882 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3883 final ServiceValidator v = new ServiceValidator();
3884 if (v.delegate(trans.org(),rd).err()) {
3885 return Result.err(Status.ERR_BadData,v.errs());
3888 final DelegateDAO.Data dd = rd.value;
3890 Result<List<DelegateDAO.Data>> ddr = ques.delegateDAO().read(trans, dd);
3891 if (access==Access.create && ddr.isOKhasData()) {
3892 return Result.err(Status.ERR_ConflictAlreadyExists, "[%s] already delegates to [%s]", dd.user, ddr.value.get(0).delegate);
3893 } else if (access!=Access.create && ddr.notOKorIsEmpty()) {
3894 return Result.err(Status.ERR_NotFound, "[%s] does not have a Delegate Record to [%s].",dd.user,access.name());
3896 Result<Void> rv = ques.mayUser(trans, dd, access);
3901 Result<FutureDAO.Data> fd = mapper.future(trans,DelegateDAO.TABLE,base, dd, false,
3903 StringBuilder sb = new StringBuilder();
3904 sb.append(access.name());
3905 sb.setCharAt(0, Character.toUpperCase(sb.charAt(0)));
3906 sb.append("Delegate ");
3907 sb.append(access==Access.create?"[":"to [");
3908 sb.append(rd.value.delegate);
3909 sb.append("] for [");
3910 sb.append(rd.value.user);
3912 return sb.toString();
3915 return Result.ok(); // Validate in code above
3920 Result<String> rfc = func.createFuture(trans, fd.value,
3921 dd.user, trans.user(),null, access==Access.create?FUTURE_OP.C:FUTURE_OP.U);
3923 return Result.err(Status.ACC_Future, "Delegate for [%s]",
3926 return Result.err(rfc);
3928 case Status.ACC_Now:
3929 if (access==Access.create) {
3930 Result<DelegateDAO.Data> rdr = ques.delegateDAO().create(trans, dd);
3934 return Result.err(rdr);
3937 return ques.delegateDAO().update(trans, dd);
3940 return Result.err(fd);
3945 public Result<Void> deleteDelegate(AuthzTrans trans, REQUEST base) {
3946 final Result<DelegateDAO.Data> rd = mapper.delegate(trans, base);
3947 final Validator v = new ServiceValidator();
3948 if (v.notOK(rd).nullOrBlank("User", rd.value.user).err()) {
3949 return Result.err(Status.ERR_BadData,v.errs());
3952 Result<List<DelegateDAO.Data>> ddl;
3953 if ((ddl=ques.delegateDAO().read(trans, rd.value)).notOKorIsEmpty()) {
3954 return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3956 final DelegateDAO.Data dd = ddl.value.get(0);
3957 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3962 return ques.delegateDAO().delete(trans, dd, false);
3966 public Result<Void> deleteDelegate(AuthzTrans trans, String userName) {
3967 DelegateDAO.Data dd = new DelegateDAO.Data();
3968 final Validator v = new ServiceValidator();
3969 if (v.nullOrBlank("User", userName).err()) {
3970 return Result.err(Status.ERR_BadData,v.errs());
3973 Result<List<DelegateDAO.Data>> ddl;
3974 if ((ddl=ques.delegateDAO().read(trans, dd)).notOKorIsEmpty()) {
3975 return Result.err(Status.ERR_DelegateNotFound,"Cannot delete non-existent Delegate");
3977 dd = ddl.value.get(0);
3978 Result<Void> rv = ques.mayUser(trans, dd, Access.write);
3983 return ques.delegateDAO().delete(trans, dd, false);
3987 public Result<DELGS> getDelegatesByUser(AuthzTrans trans, String user) {
3988 final Validator v = new ServiceValidator();
3989 if (v.nullOrBlank("User", user).err()) {
3990 return Result.err(Status.ERR_BadData,v.errs());
3993 DelegateDAO.Data ddd = new DelegateDAO.Data();
3995 ddd.delegate = null;
3996 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
3998 return Result.err(rv);
4001 TimeTaken tt = trans.start("Get delegates for a user", Env.SUB);
4003 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO().read(trans, user);
4005 if (dbDelgs.isOKhasData()) {
4006 return mapper.delegate(dbDelgs.value);
4008 return Result.err(Status.ERR_DelegateNotFound,"No Delegate found for [%s]",user);
4016 public Result<DELGS> getDelegatesByDelegate(AuthzTrans trans, String delegate) {
4017 final Validator v = new ServiceValidator();
4018 if (v.nullOrBlank("Delegate", delegate).err()) {
4019 return Result.err(Status.ERR_BadData,v.errs());
4022 DelegateDAO.Data ddd = new DelegateDAO.Data();
4023 ddd.user = delegate;
4024 Result<Void> rv = ques.mayUser(trans, ddd, Access.read);
4026 return Result.err(rv);
4029 TimeTaken tt = trans.start("Get users for a delegate", Env.SUB);
4031 Result<List<DelegateDAO.Data>> dbDelgs = ques.delegateDAO().readByDelegate(trans, delegate);
4033 if (dbDelgs.isOKhasData()) {
4034 return mapper.delegate(dbDelgs.value);
4036 return Result.err(Status.ERR_DelegateNotFound,"Delegate [%s] is not delegating for anyone.",delegate);
4043 /***********************************
4045 ***********************************/
4046 private static final String APPR_FMT = "actor=%s, action=%s, operation=\"%s\", requestor=%s, delegator=%s";
4048 public Result<Void> updateApproval(AuthzTrans trans, APPROVALS approvals) {
4049 Result<List<ApprovalDAO.Data>> rlad = mapper.approvals(approvals);
4051 return Result.err(rlad);
4053 int numApprs = rlad.value.size();
4055 return Result.err(Status.ERR_NoApprovals,"No Approvals sent for Updating");
4057 int numProcessed = 0;
4058 String user = trans.user();
4060 Result<List<ApprovalDAO.Data>> curr;
4061 Lookup<List<ApprovalDAO.Data>> apprByTicket=null;
4062 for (ApprovalDAO.Data updt : rlad.value) {
4063 if (updt.ticket!=null) {
4064 curr = ques.approvalDAO().readByTicket(trans, updt.ticket);
4065 if (curr.isOKhasData()) {
4066 final List<ApprovalDAO.Data> add = curr.value;
4067 // Store a Pre-Lookup
4068 apprByTicket = (trans1, noop) -> add;
4070 } else if (updt.id!=null) {
4071 curr = ques.approvalDAO().read(trans, updt);
4072 } else if (updt.approver!=null) {
4073 curr = ques.approvalDAO().readByApprover(trans, updt.approver);
4075 return Result.err(Status.ERR_BadData,"Approvals need ID, Ticket or Approval data to update");
4078 if (curr.isOKhasData()) {
4079 Map<String, Result<List<DelegateDAO.Data>>> delegateCache = new HashMap<>();
4080 Map<UUID, FutureDAO.Data> futureCache = new HashMap<>();
4081 FutureDAO.Data hasDeleted = new FutureDAO.Data();
4083 for (ApprovalDAO.Data cd : curr.value) {
4084 if ("pending".equals(cd.status)) {
4085 // Check for right record. Need ID, or (Ticket&Trans.User==Appr)
4087 boolean delegatedAction = ques.isDelegated(trans, user, cd.approver, delegateCache);
4088 String delegator = cd.approver;
4089 if (updt.id!=null ||
4090 (updt.ticket!=null && user.equals(cd.approver)) ||
4091 (updt.ticket!=null && delegatedAction)) {
4092 if (updt.ticket.equals(cd.ticket)) {
4093 Changed ch = new Changed();
4094 cd.id = ch.changed(cd.id,updt.id);
4095 // cd.ticket = changed(cd.ticket,updt.ticket);
4096 cd.user = ch.changed(cd.user,updt.user);
4097 cd.approver = ch.changed(cd.approver,updt.approver);
4098 cd.type = ch.changed(cd.type,updt.type);
4099 cd.status = ch.changed(cd.status,updt.status);
4100 cd.memo = ch.changed(cd.memo,updt.memo);
4101 cd.operation = ch.changed(cd.operation,updt.operation);
4102 cd.updated = ch.changed(cd.updated,updt.updated==null?new Date():updt.updated);
4103 // if (updt.status.equals("denied")) {
4104 // cd.last_notified = null;
4106 if (cd.ticket!=null) {
4107 FutureDAO.Data fdd = futureCache.get(cd.ticket);
4108 if (fdd==null) { // haven't processed ticket yet
4109 Result<FutureDAO.Data> rfdd = ques.futureDAO().readPrimKey(trans, cd.ticket);
4111 fdd = rfdd.value; // null is ok
4115 futureCache.put(cd.ticket, fdd); // processed this Ticket... don't do others on this ticket
4117 if (fdd==hasDeleted) { // YES, by Object
4119 cd.status = "ticketDeleted";
4120 ch.hasChanged(true);
4122 FUTURE_OP fop = FUTURE_OP.toFO(cd.operation);
4124 trans.info().printf("Approval Status %s is not actionable",cd.status);
4125 } else if (apprByTicket!=null) {
4126 Result<OP_STATUS> rv = func.performFutureOp(trans, fop, fdd, apprByTicket,func.urDBLookup);
4130 if (delegatedAction) {
4131 trans.audit().printf(APPR_FMT,user,updt.status,cd.memo,cd.user,delegator);
4133 futureCache.put(cd.ticket, hasDeleted);
4137 ch.hasChanged(true);
4138 trans.audit().printf(APPR_FMT,user,rv.value.desc(),cd.memo,cd.user,delegator);
4139 futureCache.put(cd.ticket, hasDeleted);
4144 trans.info().log(rv.toString());
4151 if (ch.hasChanged()) {
4152 ques.approvalDAO().update(trans, cd, true);
4161 if (numApprs==numProcessed) {
4164 return Result.err(Status.ERR_ActionNotCompleted,numProcessed + " out of " + numApprs + " completed");
4168 private static class Changed {
4169 private boolean hasChanged = false;
4171 public<T> T changed(T src, T proposed) {
4172 if (proposed==null || (src!=null && src.equals(proposed))) {
4179 public void hasChanged(boolean b) {
4183 public boolean hasChanged() {
4189 public Result<APPROVALS> getApprovalsByUser(AuthzTrans trans, String user) {
4190 final Validator v = new ServiceValidator();
4191 if (v.nullOrBlank("User", user).err()) {
4192 return Result.err(Status.ERR_BadData,v.errs());
4195 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByUser(trans, user);
4197 return mapper.approvals(rapd.value);
4199 return Result.err(rapd);
4204 public Result<APPROVALS> getApprovalsByTicket(AuthzTrans trans, String ticket) {
4205 final Validator v = new ServiceValidator();
4206 if (v.nullOrBlank("Ticket", ticket).err()) {
4207 return Result.err(Status.ERR_BadData,v.errs());
4211 uuid = UUID.fromString(ticket);
4212 } catch (IllegalArgumentException e) {
4213 return Result.err(Status.ERR_BadData,e.getMessage());
4216 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByTicket(trans, uuid);
4218 return mapper.approvals(rapd.value);
4220 return Result.err(rapd);
4225 public Result<APPROVALS> getApprovalsByApprover(AuthzTrans trans, String approver) {
4226 final Validator v = new ServiceValidator();
4227 if (v.nullOrBlank("Approver", approver).err()) {
4228 return Result.err(Status.ERR_BadData,v.errs());
4231 List<ApprovalDAO.Data> listRapds = new ArrayList<>();
4233 Result<List<ApprovalDAO.Data>> myRapd = ques.approvalDAO().readByApprover(trans, approver);
4234 if (myRapd.notOK()) {
4235 return Result.err(myRapd);
4238 listRapds.addAll(myRapd.value);
4240 Result<List<DelegateDAO.Data>> delegatedFor = ques.delegateDAO().readByDelegate(trans, approver);
4241 if (delegatedFor.isOK()) {
4242 for (DelegateDAO.Data dd : delegatedFor.value) {
4243 if (dd.expires.after(new Date())) {
4244 String delegator = dd.user;
4245 Result<List<ApprovalDAO.Data>> rapd = ques.approvalDAO().readByApprover(trans, delegator);
4247 for (ApprovalDAO.Data d : rapd.value) {
4248 if (!d.user.equals(trans.user())) {
4257 return mapper.approvals(listRapds);
4261 * @see org.onap.aaf.auth.service.AuthzService#clearCache(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String)
4264 public Result<Void> cacheClear(AuthzTrans trans, String cname) {
4265 if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4266 return ques.clearCache(trans,cname);
4268 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4269 trans.user(),ROOT_NS,CACHE,cname);
4273 * @see org.onap.aaf.auth.service.AuthzService#cacheClear(org.onap.aaf.auth.env.test.AuthzTrans, java.lang.String, java.lang.Integer)
4276 public Result<Void> cacheClear(AuthzTrans trans, String cname, int[] segment) {
4277 if (ques.isGranted(trans,trans.user(),ROOT_NS,CACHE,cname,"clear")) {
4278 Result<Void> v=null;
4279 for (int i: segment) {
4280 v=ques.cacheClear(trans,cname,i);
4286 return Result.err(Status.ERR_Denied, "%s does not have AAF Permission '%s.%s|%s|clear",
4287 trans.user(),ROOT_NS,CACHE,cname);
4291 * @see org.onap.aaf.auth.service.AuthzService#dbReset(org.onap.aaf.auth.env.test.AuthzTrans)
4294 public void dbReset(AuthzTrans trans) {
4295 ques.historyDAO().reportPerhapsReset(trans, null);