From 889a9891f6df5bbb26a760cfb106be947e87aa5c Mon Sep 17 00:00:00 2001 From: Instrumental Date: Wed, 13 Mar 2019 17:33:51 -0500 Subject: [PATCH] Batch Test improvements Issue-ID: AAF-776 Change-Id: Ia12106331a1db608955abd353785ab2e765b3c41 Signed-off-by: Instrumental --- .../main/java/org/onap/aaf/auth/batch/Batch.java | 9 +- .../aaf/auth/batch/approvalsets/ApprovalSet.java | 14 ++ .../onap/aaf/auth/batch/approvalsets/Pending.java | 18 +- .../aaf/auth/batch/approvalsets/URApprovalSet.java | 8 +- .../org/onap/aaf/auth/batch/helpers/CQLBatch.java | 4 + .../onap/aaf/auth/batch/helpers/ExpireRange.java | 11 +- .../org/onap/aaf/auth/batch/helpers/Future.java | 18 +- .../onap/aaf/auth/batch/helpers/LastNotified.java | 72 +++++-- .../onap/aaf/auth/batch/helpers/Notification.java | 211 --------------------- .../org/onap/aaf/auth/batch/helpers/UserRole.java | 22 ++- .../org/onap/aaf/auth/batch/reports/Analyze.java | 200 +++++++++++-------- .../org/onap/aaf/auth/batch/reports/Notify.java | 129 ++++++++++++- .../auth/batch/reports/bodies/NotifyCredBody.java | 22 ++- .../reports/bodies/OneMonthNotifyCredBody.java | 8 + .../reports/bodies/TwoMonthNotifyCredBody.java | 10 + .../reports/bodies/TwoWeeksNotifyCredBody.java | 8 + .../org/onap/aaf/auth/batch/update/Approvals.java | 150 +++++---------- .../org/onap/aaf/auth/batch/update/Extend.java | 2 +- .../aaf/auth/batch/test/JU_NotificationTest.java | 77 -------- auth/auth-cass/cass_init/init.cql | 9 +- auth/auth-cass/cass_init/init2_10.cql | 10 + .../main/java/org/onap/aaf/auth/cm/ca/LocalCA.java | 2 +- .../org/onap/aaf/auth/gui/pages/ApprovalForm.java | 40 ++-- .../onap/aaf/auth/gui/table/TextToolTipCell.java | 51 +++++ .../java/org/onap/aaf/cadi/filter/CadiFilter.java | 9 +- .../src/main/java/org/onap/aaf/cadi/util/CSV.java | 23 ++- 26 files changed, 566 insertions(+), 571 deletions(-) delete mode 100644 auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Notification.java delete mode 100644 auth/auth-batch/src/test/java/org/onap/aaf/auth/batch/test/JU_NotificationTest.java create mode 100644 auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/table/TextToolTipCell.java diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/Batch.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/Batch.java index 1c65c058..a588b808 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/Batch.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/Batch.java @@ -149,7 +149,7 @@ public abstract class Batch { } protected abstract void run(AuthzTrans trans); - protected abstract void _close(AuthzTrans trans); + protected void _close(AuthzTrans trans) {} public String[] args() { return env.get(ssargs); @@ -363,7 +363,12 @@ public abstract class Batch { public final void close(AuthzTrans trans) { _close(trans); - cluster.close(); + if(session!=null) { + session.close(); + } + if(cluster!=null) { + cluster.close(); + } } public static void main(String[] args) { diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/ApprovalSet.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/ApprovalSet.java index b7176c26..500906d0 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/ApprovalSet.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/ApprovalSet.java @@ -25,7 +25,9 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.UUID; import org.onap.aaf.auth.dao.cass.ApprovalDAO; @@ -93,4 +95,16 @@ public class ApprovalSet { } return errs==null?Result.ok():Result.err(Result.ERR_Backend,errs.toString()); } + + public boolean hasApprovals() { + return !ladd.isEmpty(); + } + + public Set approvers() { + Set rv = new HashSet<>(); + for(ApprovalDAO.Data app : ladd) { + rv.add(app.approver); + } + return rv; + } } \ No newline at end of file diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/Pending.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/Pending.java index 3072038a..5d720c2f 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/Pending.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/Pending.java @@ -34,15 +34,6 @@ public class Pending { boolean hasNew; Date earliest; - /** - * Use this Constructor when there is no Last Notified Date - */ - public Pending() { - qty = 1; - hasNew = true; - earliest = null; - } - /** * Use this constructor to indicate when last Notified * @param last_notified @@ -84,6 +75,11 @@ public class Pending { public void inc(Pending value) { qty+=value.qty; + if(earliest==null) { + earliest = value.earliest; + } else if(value.earliest!=null && value.earliest.before(earliest)) { + earliest = value.earliest; + } } public void earliest(Date lastnotified) { @@ -106,4 +102,8 @@ public class Pending { return hasNew; } + public static Pending create() { + return new Pending((Date)null); + } + } \ No newline at end of file diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/URApprovalSet.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/URApprovalSet.java index 2c1ffe6d..7f7bff28 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/URApprovalSet.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/URApprovalSet.java @@ -49,8 +49,12 @@ public class URApprovalSet extends ApprovalSet { UserRoleDAO.Data urdd = lurdd.load(); setConstruct(urdd.bytify()); setMemo(getMemo(urdd)); - setExpires(org.expiration(null, Organization.Expiration.UserInRole)); - setTargetKey(urdd.role); + GregorianCalendar expires = org.expiration(null, Organization.Expiration.UserInRole); + if(urdd.expires.before(expires.getTime())) { + expires.setTime(urdd.expires); + } + setExpires(expires); + setTargetKey(urdd.user+'|'+urdd.role); setTargetDate(urdd.expires); Result r = dv.roleByName(trans, urdd.role); diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/CQLBatch.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/CQLBatch.java index 738c5345..9f685ad4 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/CQLBatch.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/CQLBatch.java @@ -84,4 +84,8 @@ public class CQLBatch { } execute(dryRun); } + + public String toString() { + return sb.toString(); + } } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/ExpireRange.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/ExpireRange.java index 73bff6e6..13d74c8f 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/ExpireRange.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/ExpireRange.java @@ -40,12 +40,11 @@ public class ExpireRange { public static final String ONE_WEEK = "OneWeek"; private static final String AAF_BATCH_RANGE = "aaf_batch_range."; public Map> ranges; - public final Date now; + public static final Date now = new Date(); private Range delRange; public ExpireRange(final Access access) { - now = new Date(); ranges = new HashMap<>(); int i=0; String prop = access.getProperty(AAF_BATCH_RANGE + i,null); @@ -70,6 +69,10 @@ public class ExpireRange { } } + public static Range newFutureRange() { + return new Range("Approval",1,1,0,0,GregorianCalendar.MONTH,1); + } + public Set names() { Set names = new HashSet<>(); for(List lr : ranges.values()) { @@ -90,7 +93,7 @@ public class ExpireRange { return rv; } - public class Range { + public static class Range { private final String name; private final int reportingLevel; private final int interval; // in Days @@ -138,7 +141,7 @@ public class ExpireRange { return end; } - private boolean inRange(final Date date) { + public boolean inRange(final Date date) { if(date==null) { return false; } else { diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Future.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Future.java index 13f81938..4f87e330 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Future.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Future.java @@ -23,6 +23,7 @@ package org.onap.aaf.auth.batch.helpers; +import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Date; @@ -32,6 +33,7 @@ import java.util.TreeMap; import java.util.UUID; import org.onap.aaf.auth.dao.cass.FutureDAO; +import org.onap.aaf.auth.dao.cass.UserRoleDAO; import org.onap.aaf.auth.env.AuthzTrans; import org.onap.aaf.auth.layer.Result; import org.onap.aaf.cadi.util.CSV; @@ -90,7 +92,21 @@ public class Future implements CacheChange.Data, Comparable { fdd.start = start; fdd.expires = expires; fdd.construct = construct; - role = Approval.roleFromMemo(memo); + String role = null; + switch(target) { + case "user_role": + UserRoleDAO.Data urdd = new UserRoleDAO.Data(); + try { + urdd.reconstitute(construct); + fdd.target_key = urdd.user + '|' + urdd.role; + fdd.target_date=urdd.expires; + role=urdd.role; + } catch (IOException e) { + e.printStackTrace(System.err); + } + break; + } + this.role = role; } public final UUID id() { diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java index 22231f3a..0539fcdc 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java @@ -28,7 +28,7 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; -import org.onap.aaf.auth.batch.helpers.Notification.TYPE; +import org.onap.aaf.auth.dao.cass.UserRoleDAO; import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.Row; @@ -37,6 +37,7 @@ import com.datastax.driver.core.Session; public class LastNotified { private Map lastNotified = new TreeMap<>(); private Session session; + public static final Date never = new Date(0); public LastNotified(Session session) { this.session = session; @@ -44,7 +45,7 @@ public class LastNotified { public void add(Set users) { StringBuilder query = new StringBuilder(); - startNotifyQuery(query); + startQuery(query); int cnt = 0; for(String user : users) { if(++cnt>1) { @@ -54,37 +55,74 @@ public class LastNotified { query.append(user); query.append('\''); if(cnt>=30) { - endNotifyQuery(query, Notification.TYPE.OA); + endQuery(query); add(session.execute(query.toString()),lastNotified); query.setLength(0); - startNotifyQuery(query); + startQuery(query); cnt=0; } } if(cnt>0) { - endNotifyQuery(query, Notification.TYPE.OA); + endQuery(query); add(session.execute(query.toString()),lastNotified); } } - public Date lastNotified(String user) { - return lastNotified.get(user); + /** + * Note: target_key CAN also contain a Pipe. + * + * @param user + * @param target + * @param target_key + * @return + */ + public Date lastNotified(String user, String target, String target_key) { + String key = user + '|' + target + '|' + target_key; + return lastNotified(key); } - private void add(ResultSet result, Map lastNotified) { + public Date lastNotified(String key) { + Date rv = lastNotified.get(key); + if(rv==null) { + rv = never; + lastNotified.put(key, rv); + } + return rv; + } + + private Date add(ResultSet result, Map lastNotified) { + Date last = null; for(Iterator iter = result.iterator(); iter.hasNext();) { Row r = iter.next(); - lastNotified.put(r.getString(0), r.getTimestamp(1)); + String key = r.getString(0) + '|' + + r.getString(1) + '|' + + r.getString(2); + + lastNotified.put(key, last=r.getTimestamp(3)); } + return last; } - private void startNotifyQuery(StringBuilder query) { - query.append("SELECT user,last FROM authz.notify WHERE user in ("); + private void startQuery(StringBuilder query) { + query.append("SELECT user,target,key,last FROM authz.notified WHERE user in ("); } - - private void endNotifyQuery(StringBuilder query, TYPE oa) { - query.append(") AND type="); - query.append(oa.idx()); - query.append(';'); - } + + private void endQuery(StringBuilder query) { + query.append(");"); + } + + public void update(StringBuilder query,String user, String target, String key) { + query.append("UPDATE authz.notified SET last=dateof(now()) WHERE user='"); + query.append(user); + query.append("' AND target='"); + query.append(target); + query.append("' AND key='"); + query.append(key); + query.append("';"); + } + + public static String newKey(UserRoleDAO.Data urdd) { + return urdd.user + "|ur|" + urdd.role; + } + } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Notification.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Notification.java deleted file mode 100644 index ae0d37b5..00000000 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Notification.java +++ /dev/null @@ -1,211 +0,0 @@ -/** - * ============LICENSE_START==================================================== - * org.onap.aaf - * =========================================================================== - * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. - * =========================================================================== - * Modifications Copyright (C) 2018 IBM. - * =========================================================================== - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END==================================================== - * - */ - -package org.onap.aaf.auth.batch.helpers; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.TreeMap; - -import org.onap.aaf.auth.batch.actions.Message; -import org.onap.aaf.auth.env.AuthzTrans; -import org.onap.aaf.auth.org.Organization; -import org.onap.aaf.misc.env.Env; -import org.onap.aaf.misc.env.TimeTaken; -import org.onap.aaf.misc.env.Trans; -import org.onap.aaf.misc.env.util.Chrono; - -import com.datastax.driver.core.ResultSet; -import com.datastax.driver.core.Row; -import com.datastax.driver.core.Session; -import com.datastax.driver.core.SimpleStatement; -import com.datastax.driver.core.Statement; - -public class Notification { - public enum TYPE { - OA("Owner Approval",1),SA("Supervisor Approval",2),CN("Credential Expiration",20); - - private String desc; - private int type; - - private TYPE(String desc,int type) { - this.desc = desc; - this.type = type; - } - - public String desc() { - return desc; - } - - public int idx() { - return type; - } - - public static TYPE get(int idx) { - for (TYPE nt : TYPE.values()) { - if (idx==nt.type) { - return nt; - } - } - return null; - } - } - - - public static final TreeMap> data = new TreeMap<>(); - public static final Date now = new Date(); - - public final String user; - public final TYPE type; - public Date last; - public int checkSum; - public Message msg; - private int current; - public Organization org; - public int count; - - public static Creator v2_0_18 = new Creator() { - @Override - public Notification create(Row row) { - int idx =row.getInt(1); - TYPE typeCreator = TYPE.get(idx); - if (typeCreator==null) { - return null; - } - return new Notification(row.getString(0), typeCreator, row.getTimestamp(2), row.getInt(3)); - } - - @Override - public String select() { - return "SELECT user,type,last,checksum FROM authz.notify LIMIT 100000"; - } - }; - - private Notification(String user, TYPE nt, Date last, int checksum) { - this.user = user; - this.type = nt; - this.last = last; - this.checkSum = checksum; - current = 0; - count = 0; - } - - public static void load(Trans trans, Session session, Creator creator ) { - trans.info().log( "query: " + creator.select() ); - TimeTaken tt = trans.start("Load Notify", Env.REMOTE); - - ResultSet results; - try { - Statement stmt = new SimpleStatement(creator.select()); - results = session.execute(stmt); - } finally { - tt.done(); - } - int count = 0; - tt = trans.start("Process Notify", Env.SUB); - - try { - for (Row row : results.all()) { - ++count; - try { - Notification not = creator.create(row); - List ln = data.get(not.user); - if (ln==null) { - ln = new ArrayList<>(); - data.put(not.user, ln); - } - ln.add(not); - } finally { - tt.done(); - } - } - } finally { - tt.done(); - trans.info().log("Found",count,"Notify Records"); - } - } - - public static Notification get(String user, TYPE type) { - List ln = data.get(user); - if (ln!=null) { - for (Notification n : ln) { - if (type.equals(n.type)) { - return n; - } - } - } - return null; - } - - public static Notification create(String user, TYPE type) { - return new Notification(user,type,null,0); - } - - - public void set(Message msg) { - this.msg = msg; - } - - public int checksum() { - if (msg==null) { - current=0; - } else if (current==0) { - for (String l : msg.lines) { - for (byte b : l.getBytes()) { - current+=b; - } - } - } - return current; - } - - public boolean update(AuthzTrans trans, Session session, boolean dryRun) { - checksum(); - if (last==null || current==0 || current!=checkSum) { - last = now; - current = checksum(); - String update = "UPDATE authz.notify SET " + - "last = '" + Chrono.utcStamp(last) + - "', checksum=" + - current + - " WHERE user='" + - user + - "' AND type=" + - type.idx() + - ";"; - if (dryRun) { - trans.info().log("Would",update); - } else { - session.execute(update); - } - return true; - } - return false; - } - - public String toString() { - return "\"" + user + "\",\"" + type.name() + "\",\"" - + Chrono.dateTime(last)+ "\", " + checkSum; - } -} \ No newline at end of file diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/UserRole.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/UserRole.java index 55dd1e7c..343a0e2b 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/UserRole.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/UserRole.java @@ -122,12 +122,12 @@ public class UserRole implements Cloneable, CacheChange.Data { } public static void loadOneUser(Trans trans, Session session, Creator creator, String user, Visitor visitor ) { - load(trans,session,creator,"role='"+ user +"';",visitor); + load(trans,session,creator,"user='"+ user +'\'',visitor); } private static void load(Trans trans, Session session, Creator creator, String where, Visitor visitor) { String query = creator.query(where); - trans.info().log( "query: " + query ); + trans.debug().log( "query: " + query ); TimeTaken tt = trans.start("Read UserRoles", Env.REMOTE); ResultSet results; @@ -145,7 +145,7 @@ public class UserRole implements Cloneable, CacheChange.Data { tt.done(); } } finally { - trans.info().log("Loaded",totalLoaded,"UserRoles"); + trans.debug().log("Loaded",totalLoaded,"UserRoles"); } } @@ -337,16 +337,26 @@ public class UserRole implements Cloneable, CacheChange.Data { sb.append("';\n"); } - public static void batchExtend(StringBuilder sb, List row, String newDate ) { + public static void batchExtend(StringBuilder sb, List row, Date newDate ) { sb.append("UPDATE authz.user_role SET expires='"); - sb.append(newDate); + sb.append(Chrono.dateTime(newDate)); sb.append("' WHERE user='"); sb.append(row.get(1)); sb.append("' AND role='"); sb.append(row.get(2)); sb.append("';\n"); } - + + public void batchUpdateExpires(StringBuilder sb) { + sb.append("UPDATE authz.user_role SET expires='"); + sb.append(Chrono.dateTime(expires())); + sb.append("' WHERE user='"); + sb.append(user()); + sb.append("' AND role='"); + sb.append(role()); + sb.append("';\n"); + } + public static String histMemo(String fmt, List row) { String reason; if(row.size()>7) { // Reason included diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Analyze.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Analyze.java index a0dce749..70c950ec 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Analyze.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Analyze.java @@ -38,6 +38,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; import java.util.UUID; import org.onap.aaf.auth.batch.Batch; @@ -49,6 +50,7 @@ import org.onap.aaf.auth.batch.helpers.Cred.Instance; import org.onap.aaf.auth.batch.helpers.ExpireRange; import org.onap.aaf.auth.batch.helpers.ExpireRange.Range; import org.onap.aaf.auth.batch.helpers.Future; +import org.onap.aaf.auth.batch.helpers.LastNotified; import org.onap.aaf.auth.batch.helpers.Role; import org.onap.aaf.auth.batch.helpers.UserRole; import org.onap.aaf.auth.batch.helpers.X509; @@ -87,6 +89,8 @@ public class Analyze extends Batch { private CSV.Writer deleteCW; private CSV.Writer approveCW; private CSV.Writer extendCW; + private Range futureRange; + private final String sdate; public Analyze(AuthzTrans trans) throws APIException, IOException, OrganizationException { super(trans.env()); @@ -110,14 +114,14 @@ public class Analyze extends Batch { writerList = new HashMap<>(); expireRange = new ExpireRange(trans.env().access()); - String sdate = Chrono.dateOnlyStamp(expireRange.now); + sdate = Chrono.dateOnlyStamp(ExpireRange.now); for( List lr : expireRange.ranges.values()) { for(Range r : lr ) { if(writerList.get(r.name())==null) { File file = new File(logDir(),r.name() + sdate +CSV); CSV csv = new CSV(env.access(),file); CSV.Writer cw = csv.writer(false); - cw.row(INFO,r.name(),Chrono.dateOnlyStamp(expireRange.now),r.reportingLevel()); + cw.row(INFO,r.name(),sdate,r.reportingLevel()); writerList.put(r.name(),cw); if("Delete".equals(r.name())) { deleteDate = r.getEnd(); @@ -129,17 +133,18 @@ public class Analyze extends Batch { } // Setup New Approvals file + futureRange = ExpireRange.newFutureRange(); File file = new File(logDir(),APPROVALS + sdate +CSV); CSV approveCSV = new CSV(env.access(),file); approveCW = approveCSV.writer(); - approveCW.row(INFO,APPROVALS,Chrono.dateOnlyStamp(expireRange.now),1); + approveCW.row(INFO,APPROVALS,sdate,1); writerList.put(APPROVALS,approveCW); // Setup Extend Approvals file file = new File(logDir(),EXTEND + sdate +CSV); CSV extendCSV = new CSV(env.access(),file); extendCW = extendCSV.writer(); - extendCW.row(INFO,EXTEND,Chrono.dateOnlyStamp(expireRange.now),1); + extendCW.row(INFO,EXTEND,sdate,1); writerList.put(EXTEND,extendCW); // Load full data of the following @@ -160,7 +165,7 @@ public class Analyze extends Batch { try { Future.load(noAvg, session, Future.withConstruct, fut -> { List appls = Approval.byTicket.get(fut.id()); - if(fut.expires().before(expireRange.now)) { + if(!futureRange.inRange(fut.expires())) { deleteCW.comment("Future %s expired", fut.id()); Future.row(deleteCW,fut); if(appls!=null) { @@ -179,6 +184,7 @@ public class Analyze extends Batch { tt.done(); } + Set approvers = new TreeSet<>(); tt = trans.start("Connect Approvals with Futures",Trans.SUB); try { for(Approval appr : Approval.list) { @@ -192,6 +198,7 @@ public class Analyze extends Batch { Approval.row(deleteCW, appr); } else { ticket.approvals.add(appr); // add to found Ticket + approvers.add(appr.getApprover()); } } } finally { @@ -205,78 +212,98 @@ public class Analyze extends Batch { Map pendingApprs = new HashMap<>(); Map pendingTemp = new HashMap<>(); + // Convert Good Tickets to keyed User/Role for UserRole Step + Map mur = new TreeMap<>(); + LastNotified ln = new LastNotified(session); + ln.add(approvers); + String approver; + tt = trans.start("Analyze Good Tickets",Trans.SUB); try { for(Ticket ticket : goodTickets.values()) { - pendingTemp.clear(); - switch(ticket.f.target()) { - case "user_role": - int state[][] = new int[3][3]; - int type; - - for(Approval appr : ticket.approvals) { - switch(appr.getType()) { - case "owner": - type=owner; - break; - case "supervisor": - type=supervisor; - break; - default: - type=0; + try { + pendingTemp.clear(); + switch(ticket.f.target()) { + case "user_role": + int state[][] = new int[3][3]; + int type; + + for(Approval appr : ticket.approvals) { + switch(appr.getType()) { + case "owner": + type=owner; + break; + case "supervisor": + type=supervisor; + break; + default: + type=0; + } + ++state[type][total]; // count per type + switch(appr.getStatus()) { + case "pending": + ++state[type][pending]; + approver = appr.getApprover(); + Pending n = pendingTemp.get(approver); + if(n==null) { + Date lastNotified = ln.lastNotified(approver,"ur",ticket.f.fdd.target_key); + pendingTemp.put(approver,new Pending(lastNotified)); + } else { + n.inc(); + } + break; + case "approved": + ++state[type][approved]; + break; + default: + ++state[type][unknown]; + } } - ++state[type][total]; // count per type - switch(appr.getStatus()) { - case "pending": - ++state[type][pending]; - Pending n = pendingTemp.get(appr.getApprover()); - if(n==null) { - pendingTemp.put(appr.getApprover(),new Pending()); - } else { - n.inc(); + + // To Approve: + // Always must have at least 1 owner + if((state[owner][total]>0 && state[owner][approved]>0) && + // If there are no Supervisors, that's ok + (state[supervisor][total]==0 || + // But if there is a Supervisor, they must have approved + (state[supervisor][approved]>0))) { + UserRoleDAO.Data urdd = new UserRoleDAO.Data(); + try { + urdd.reconstitute(ticket.f.fdd.construct); + if(urdd.expires.before(ticket.f.expires())) { + extendCW.row("extend_ur",urdd.user,urdd.role,ticket.f.expires()); + } + } catch (IOException e) { + trans.error().log("Could not reconstitute UserRole"); } - break; - case "approved": - ++state[type][approved]; - break; - default: - ++state[type][unknown]; - } - } - - // To Approve: - // Always must have at least 1 owner - if((state[owner][total]>0 && state[owner][approved]>0) && - // If there are no Supervisors, that's ok - (state[supervisor][total]==0 || - // But if there is a Supervisor, they must have approved - (state[supervisor][approved]>0))) { - UserRoleDAO.Data urdd = new UserRoleDAO.Data(); - try { - urdd.reconstitute(ticket.f.fdd.construct); - if(urdd.expires.before(ticket.f.expires())) { - extendCW.row("extend_ur",urdd.user,urdd.role,ticket.f.expires()); + } else { // Load all the Pending. + for(Entry es : pendingTemp.entrySet()) { + Pending p = pendingApprs.get(es.getKey()); + if(p==null) { + pendingApprs.put(es.getKey(), es.getValue()); + } else { + p.inc(es.getValue()); } - } catch (IOException e) { - trans.error().log("Could not reconstitute UserRole"); - } - } else { // Load all the Pending. - for(Entry es : pendingTemp.entrySet()) { - Pending p = pendingApprs.get(es.getKey()); - if(p==null) { - pendingApprs.put(es.getKey(), es.getValue()); - } else { - p.inc(es.getValue()); } } + break; + } + } finally { + if("user_role".equals(ticket.f.fdd.target)) { + String key = ticket.f.fdd.target_key; + if(key!=null) { + mur.put(key, ticket); } - break; + } } } } finally { tt.done(); } - + + // Good Tickets no longer needed + goodTickets.clear(); + /** * Decide to Notify about Approvals, based on activity/last Notified */ @@ -288,7 +315,9 @@ public class Analyze extends Batch { for(Entry es : pendingApprs.entrySet()) { Pending p = es.getValue(); - if(p.newApprovals() || p.earliest() == null || p.earliest().after(remind)) { + if(p.newApprovals() + || p.earliest() == null + || p.earliest().after(remind)) { p.row(approveCW,es.getKey()); } } @@ -297,7 +326,6 @@ public class Analyze extends Batch { } // clear out Approval Intermediates - goodTickets.clear(); pendingTemp = null; pendingApprs = null; @@ -309,7 +337,7 @@ public class Analyze extends Batch { try { tt = trans.start("Analyze UserRoles, storing Owners",Trans.SUB); Set specialCommented = new HashSet<>(); - Map> owners = new TreeMap>(); + Map> owners = new TreeMap<>(); try { UserRole.load(noAvg, session, UserRole.v2_0_11, ur -> { Identity identity; @@ -340,20 +368,25 @@ public class Analyze extends Batch { ur.row(deleteCW, UserRole.UR,String.format("Role %s does not exist", ur.role())); return; } - // Cannot just delete owners, unless there is at least one left. Process later - if ("owner".equals(ur.rname())) { - Set urs = owners.get(ur.role()); - if (urs == null) { - urs = new HashSet(); - owners.put(ur.role(), urs); - } - urs.add(ur); - } else { - Range r = writeAnalysis(noAvg,ur); - if(r!=null) { - Approval existing = findApproval(ur); - if(existing==null) { - ur.row(approveCW,UserRole.APPROVE_UR); + // Just let expired UserRoles sit until deleted + if(futureRange.inRange(ur.expires())) { + if(!mur.containsKey(ur.user() + '|' + ur.role())) { + // Cannot just delete owners, unless there is at least one left. Process later + if ("owner".equals(ur.rname())) { + Set urs = owners.get(ur.role()); + if (urs == null) { + urs = new HashSet(); + owners.put(ur.role(), urs); + } + urs.add(ur); + } else { + Range r = writeAnalysis(noAvg,ur); + if(r!=null) { + Approval existing = findApproval(ur); + if(existing==null) { + ur.row(approveCW,UserRole.APPROVE_UR); + } + } } } } @@ -374,16 +407,16 @@ public class Analyze extends Batch { tt = trans.start("Analyze Owners Separately",Trans.SUB); try { if (!owners.values().isEmpty()) { - File file = new File(logDir(), EXPIRED_OWNERS + Chrono.dateOnlyStamp(expireRange.now) + CSV); + File file = new File(logDir(), EXPIRED_OWNERS + sdate + CSV); final CSV ownerCSV = new CSV(env.access(),file); CSV.Writer expOwner = ownerCSV.writer(); - expOwner.row(INFO,EXPIRED_OWNERS,Chrono.dateOnlyStamp(expireRange.now),2); + expOwner.row(INFO,EXPIRED_OWNERS,sdate,2); try { for (Set sur : owners.values()) { int goodOwners = 0; for (UserRole ur : sur) { - if (ur.expires().after(expireRange.now)) { + if (ur.expires().after(ExpireRange.now)) { ++goodOwners; } } @@ -462,7 +495,6 @@ public class Analyze extends Batch { } catch (CertificateException | IOException e) { noAvg.error().log(e, "Error Decrypting X509"); } - }); } finally { tt.done(); diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Notify.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Notify.java index 1c1f660c..7cddea2d 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Notify.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Notify.java @@ -25,22 +25,36 @@ import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.text.ParseException; import java.util.ArrayList; +import java.util.Date; +import java.util.GregorianCalendar; import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import java.util.TreeMap; import org.onap.aaf.auth.batch.Batch; +import org.onap.aaf.auth.batch.approvalsets.Pending; +import org.onap.aaf.auth.batch.helpers.CQLBatch; +import org.onap.aaf.auth.batch.helpers.CQLBatchLoop; +import org.onap.aaf.auth.batch.helpers.LastNotified; import org.onap.aaf.auth.batch.reports.bodies.NotifyBody; +import org.onap.aaf.auth.batch.reports.bodies.NotifyPendingApprBody; import org.onap.aaf.auth.env.AuthzTrans; import org.onap.aaf.auth.org.Mailer; import org.onap.aaf.auth.org.Organization.Identity; import org.onap.aaf.auth.org.OrganizationException; import org.onap.aaf.cadi.Access; import org.onap.aaf.cadi.CadiException; +import org.onap.aaf.cadi.PropAccess; import org.onap.aaf.cadi.client.Holder; import org.onap.aaf.cadi.util.CSV; import org.onap.aaf.misc.env.APIException; +import org.onap.aaf.misc.env.TimeTaken; +import org.onap.aaf.misc.env.Trans; import org.onap.aaf.misc.env.util.Chrono; public class Notify extends Batch { @@ -52,9 +66,15 @@ import org.onap.aaf.misc.env.util.Chrono; private final int indent; private final boolean urgent; public final String guiURL; + private PropAccess access; + private AuthzTrans noAvg; + private CQLBatch cqlBatch; public Notify(AuthzTrans trans) throws APIException, IOException, OrganizationException { super(trans.env()); + access = env.access(); + session = super.cluster.connect(); + String mailerCls = env.getProperty("MAILER"); String mailFrom = env.getProperty("MAIL_FROM"); String header_html = env.getProperty("HEADER_HTML"); @@ -82,12 +102,14 @@ import org.onap.aaf.misc.env.util.Chrono; sb.append('\n'); } String html_css = env.getProperty(HTML_CSS); + String temp; int hc = sb.indexOf(HTML_CSS); if(hc!=0 && html_css!=null) { - header = sb.replace(hc,hc+HTML_CSS.length(), html_css).toString(); + temp = sb.replace(hc,hc+HTML_CSS.length(), html_css).toString(); } else { - header = sb.toString(); + temp = sb.toString(); } + header = temp.replace("AAF:ENV", batchEnv); } finally { br.close(); } @@ -119,6 +141,8 @@ import org.onap.aaf.misc.env.util.Chrono; br.close(); } + noAvg = trans.env().newTransNoAvg(); + cqlBatch = new CQLBatch(noAvg.info(),session); } /* @@ -127,10 +151,10 @@ import org.onap.aaf.misc.env.util.Chrono; */ @Override protected void run(AuthzTrans trans) { - AuthzTrans noAvg = trans.env().newTransNoAvg(); final Holder> info = new Holder<>(null); final Set errorSet = new HashSet<>(); + String fmt = "%s"+Chrono.dateOnlyStamp()+".csv"; try { // Class Load possible data @@ -145,7 +169,6 @@ import org.onap.aaf.misc.env.util.Chrono; notifyFile.add(new File(logDir, args()[i])); } } else { - String fmt = "%s"+Chrono.dateOnlyStamp()+".csv"; File file; for(NotifyBody nb : NotifyBody.getAll()) { file = new File(logDir,String.format(fmt, nb.name())); @@ -188,6 +211,100 @@ import org.onap.aaf.misc.env.util.Chrono; for(NotifyBody nb : NotifyBody.getAll()) { notify(noAvg, nb); } + + // + // Do Pending Approval Notifies. We do this separately, because we are consolidating + // all the new entries, etc. + // + List csvList = new ArrayList<>(); + for(String s : new String[] {"Approvals","ApprovalsNew"}) { + File f = new File(logDir(),String.format(fmt, s)); + if(f.exists()) { + csvList.add(new CSV(access,f)); + } + } + + Map mpending = new TreeMap<>(); + Holder count = new Holder<>(0); + for(CSV approveCSV : csvList) { + TimeTaken tt = trans.start("Load Analyzed Reminders",Trans.SUB,approveCSV.name()); + try { + approveCSV.visit(row -> { + switch(row.get(0)) { +// case "info": +// break; + case Pending.REMIND: + try { + String user = row.get(1); + Pending p = new Pending(row); + Pending mp = mpending.get(user); + if(mp==null) { + mpending.put(user, p); + } else { + mp.inc(p); // FYI, unlikely + } + count.set(count.get()+1); + } catch (ParseException e) { + trans.error().log(e); + } + break; + } + }); + } catch (IOException | CadiException e) { + e.printStackTrace(); + // .... but continue with next row + } finally { + tt.done(); + } + } + trans.info().printf("Read %d Reminder Rows", count.get()); + + NotifyPendingApprBody npab = new NotifyPendingApprBody(access); + + GregorianCalendar gc = new GregorianCalendar(); + gc.add(GregorianCalendar.DAY_OF_MONTH, 7); // Get from INFO? + Date oneWeek = gc.getTime(); + CSV.Saver rs = new CSV.Saver(); + + TimeTaken tt = trans.start("Obtain Last Notifications for Approvers", Trans.SUB); + LastNotified lastN; + try { + lastN = new LastNotified(session); + lastN.add(mpending.keySet()); + } finally { + tt.done(); + } + + Pending p; + final CQLBatchLoop cbl = new CQLBatchLoop(cqlBatch,50,dryRun); + tt = trans.start("Notify for Pending", Trans.SUB); + try { + for(Entry es : mpending.entrySet()) { + p = es.getValue(); + boolean nap = p.newApprovals(); + if(!nap) { + Date dateLastNotified = lastN.lastNotified(es.getKey(),"pending",""); + if(dateLastNotified==null || dateLastNotified.after(oneWeek) ) { + nap=true; + } + } + if(nap) { + rs.row("appr", es.getKey(),p.qty(),batchEnv); + npab.store(rs.asList()); + if(notify(noAvg, npab)>0) { + // Update + cbl.preLoop(); + lastN.update(cbl.inc(),es.getKey(),"pending",""); + } + } + } + } finally { + cbl.flush(); + tt.done(); + } + trans.info().printf("Created %d Notifications", count.get()); + + } catch (APIException | IOException e1) { trans.error().log(e1); @@ -272,8 +389,4 @@ import org.onap.aaf.misc.env.util.Chrono; return nb.count(); } - @Override - protected void _close(AuthzTrans trans) { - } - } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyCredBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyCredBody.java index 94502d9f..15a104d1 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyCredBody.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyCredBody.java @@ -32,15 +32,17 @@ import org.onap.aaf.misc.env.util.Chrono; public abstract class NotifyCredBody extends NotifyBody { private final String explanation; + private final String instruction; + public NotifyCredBody(Access access, String name) throws IOException { super(access,"cred",name); // Default explanation = "The following Credentials that you are responsible for " - + "are expiring on the dates shown. " - + "Failure to act before the expiration date will cause your App's " - + "Authentications to fail." - + "

Instructions for 'Password':

    " + + "are expiring on the dates shown.

    " + ; + + instruction = "

    Instructions for 'Password':

      " + "
    • Click on the Fully Qualified ID to ADD a new Password
    • " + "
    • REMEMBER! You are not finished until you
        " + "
      1. CHANGE ALL the configurations on ALL your processes!!
      2. " @@ -48,10 +50,20 @@ public abstract class NotifyCredBody extends NotifyBody { + "
      3. IF there is a WARNING, click the link for more information
      4. " + "
    "; } + + /** + * Default Dynamic Text. Override is expected + * @return + */ + protected String dynamic() { + return "Failure to act before the expiration date will cause your App's Authentications to fail."; + } @Override public boolean body(AuthzTrans trans, StringBuilder sb, int indent, Notify n, String id) { - println(sb,indent,explanation); + print(sb,indent,explanation); + print(sb,indent,dynamic()); + println(sb,indent,instruction); println(sb,indent,""); indent+=2; println(sb,indent,""); diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/OneMonthNotifyCredBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/OneMonthNotifyCredBody.java index a288f495..866dc23c 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/OneMonthNotifyCredBody.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/OneMonthNotifyCredBody.java @@ -34,4 +34,12 @@ public class OneMonthNotifyCredBody extends NotifyCredBody { public String subject() { return String.format("AAF One Month Credential Notification (ENV: %s)",env); } + + /* (non-Javadoc) + * @see org.onap.aaf.auth.batch.reports.bodies.NotifyCredBody#dynamic() + */ + @Override + protected String dynamic() { + return "This is your one month notification. " + super.dynamic(); + } } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java index eb52c6d0..98ee47d9 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java @@ -34,4 +34,14 @@ public class TwoMonthNotifyCredBody extends NotifyCredBody { public String subject() { return String.format("AAF Two Month Credential Notification (ENV: %s)",env); } + + /* (non-Javadoc) + * @see org.onap.aaf.auth.batch.reports.bodies.NotifyCredBody#dynamic() + */ + @Override + protected String dynamic() { + return "This is a friendly, 2 month reminder to schedule appropriate creation and deployment " + + "of your credentials, and modification of your configurations on a per instance basis. " + + " Use the following text to help create your Ticket."; + } } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoWeeksNotifyCredBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoWeeksNotifyCredBody.java index 818556c7..46188568 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoWeeksNotifyCredBody.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoWeeksNotifyCredBody.java @@ -34,4 +34,12 @@ public class TwoWeeksNotifyCredBody extends NotifyCredBody { public String subject() { return String.format("AAF Two Week Credential Notification (ENV: %s)",env); } + + /* (non-Javadoc) + * @see org.onap.aaf.auth.batch.reports.bodies.NotifyCredBody#dynamic() + */ + @Override + protected String dynamic() { + return "You have now reached critical stage. This email is escalated to your superiors. " + super.dynamic(); + } } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Approvals.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Approvals.java index 03c812ae..0df49343 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Approvals.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Approvals.java @@ -23,13 +23,12 @@ package org.onap.aaf.auth.batch.update; import java.io.File; import java.io.IOException; -import java.text.ParseException; import java.util.ArrayList; -import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; import java.util.TreeMap; import org.onap.aaf.auth.batch.Batch; @@ -38,42 +37,34 @@ import org.onap.aaf.auth.batch.approvalsets.ApprovalSet; import org.onap.aaf.auth.batch.approvalsets.Pending; import org.onap.aaf.auth.batch.approvalsets.URApprovalSet; import org.onap.aaf.auth.batch.helpers.BatchDataView; -import org.onap.aaf.auth.batch.helpers.CQLBatch; -import org.onap.aaf.auth.batch.helpers.CQLBatchLoop; -import org.onap.aaf.auth.batch.helpers.LastNotified; import org.onap.aaf.auth.batch.helpers.NS; -import org.onap.aaf.auth.batch.helpers.Notification; -import org.onap.aaf.auth.batch.helpers.Notification.TYPE; import org.onap.aaf.auth.batch.helpers.Role; import org.onap.aaf.auth.batch.helpers.UserRole; -import org.onap.aaf.auth.batch.reports.Notify; -import org.onap.aaf.auth.batch.reports.bodies.NotifyPendingApprBody; import org.onap.aaf.auth.dao.cass.UserRoleDAO; import org.onap.aaf.auth.env.AuthzTrans; import org.onap.aaf.auth.layer.Result; import org.onap.aaf.auth.org.OrganizationException; -import org.onap.aaf.cadi.Access; import org.onap.aaf.cadi.CadiException; import org.onap.aaf.cadi.client.Holder; import org.onap.aaf.cadi.util.CSV; +import org.onap.aaf.cadi.util.CSV.Writer; import org.onap.aaf.misc.env.APIException; import org.onap.aaf.misc.env.TimeTaken; import org.onap.aaf.misc.env.Trans; import org.onap.aaf.misc.env.util.Chrono; public class Approvals extends Batch { - private final Access access; private final AuthzTrans noAvg; private BatchDataView dataview; private List csvList; - private GregorianCalendar now; - private final Notify notify; + private Writer napproveCW; + private static final GregorianCalendar now = new GregorianCalendar(); + private static final String sdate = Chrono.dateOnlyStamp(now); + private static final String CSV = ".csv"; + private static final String APPROVALS_NEW = "ApprovalsNew"; - public Approvals(AuthzTrans trans) throws APIException, IOException, OrganizationException { super(trans.env()); - notify = new Notify(trans); - access = env.access(); noAvg = env.newTransNoAvg(); noAvg.setUser(new BatchPrincipal("batch:Approvals")); session = cluster.connect(); @@ -82,8 +73,6 @@ public class Approvals extends Batch { Role.load(trans, session); UserRole.load(trans, session, UserRole.v2_0_11); - now = new GregorianCalendar(); - csvList = new ArrayList<>(); File f; if(args().length>0) { @@ -103,45 +92,21 @@ public class Approvals extends Batch { trans.error().printf("CSV File %s does not exist",f.getAbsolutePath()); } } + + + File file = new File(logDir(),APPROVALS_NEW + sdate +CSV); + CSV approveCSV = new CSV(env.access(),file); + napproveCW = approveCSV.writer(); + napproveCW.row("info",APPROVALS_NEW,sdate,1); + } @Override protected void run(AuthzTrans trans) { Map mpending = new TreeMap<>(); - Holder count = new Holder<>(0); - final CQLBatchLoop cbl = new CQLBatchLoop(new CQLBatch(noAvg.info(),session),100,dryRun); - for(CSV approveCSV : csvList) { - TimeTaken tt = trans.start("Load Analyzed Reminders",Trans.SUB,approveCSV.name()); - try { - approveCSV.visit(row -> { - switch(row.get(0)) { - case Pending.REMIND: - try { - String user = row.get(1); - Pending p = new Pending(row); - Pending mp = mpending.get(user); - if(mp==null) { - mpending.put(user, p); - } else { - mp.inc(p); // FYI, unlikely - } - count.set(count.get()+1); - } catch (ParseException e) { - trans.error().log(e); - } - break; - } - }); - } catch (IOException | CadiException e) { - e.printStackTrace(); - // .... but continue with next row - } finally { - tt.done(); - } - } - trans.info().printf("Processed %d Reminder Rows", count.get()); + Pending p = Pending.create(); - count.set(0); + Holder count = new Holder<>(0); for(CSV approveCSV : csvList) { TimeTaken tt = trans.start("Processing %s's UserRoles",Trans.SUB,approveCSV.name()); try { @@ -155,14 +120,20 @@ public class Approvals extends Batch { }); Result rw = uras.write(noAvg); if(rw.isOK()) { - Pending p = new Pending(); - Pending mp = mpending.get(urdd.user); - if(mp==null) { - mpending.put(urdd.user, p); + Set approvers = uras.approvers(); + if(approvers.isEmpty()) { + trans.error().printf("No Approvers found for %s-%s (probably no owner)",urdd.user,urdd.role); } else { - mp.inc(p); + for(String approver : approvers) { + Pending mp = mpending.get(approver); + if(mp==null) { + mpending.put(approver, Pending.create()); + } else { + mp.inc(p); // FYI, unlikely + } + } + count.set(count.get()+1); } - count.set(count.get()+1); } else { trans.error().log(rw.errorString()); } @@ -178,60 +149,25 @@ public class Approvals extends Batch { } trans.info().printf("Processed %d UserRoles", count.get()); - count.set(0); - NotifyPendingApprBody npab = new NotifyPendingApprBody(access); - - GregorianCalendar gc = new GregorianCalendar(); - gc.add(GregorianCalendar.DAY_OF_MONTH, 7); - Date oneWeek = gc.getTime(); - CSV.Saver rs = new CSV.Saver(); - - tt = trans.start("Obtain Last Notifications", Trans.SUB); - LastNotified lastN; - try { - lastN = new LastNotified(session); - lastN.add(mpending.keySet()); - } finally { - tt.done(); - } - - Pending p; - tt = trans.start("Notify for Pending", Trans.SUB); + tt = trans.start("Processing %s's UserRoles",Trans.SUB,approveCSV.name()); + int cnt = 0; try { - for(Entry es : mpending.entrySet()) { - p = es.getValue(); - Date dateLastNotified = lastN.lastNotified(es.getKey()); - if(p.newApprovals() || dateLastNotified==null || dateLastNotified.after(oneWeek) ) { - rs.row("appr", es.getKey(),p.qty(),batchEnv); - npab.store(rs.asList()); - if(notify.notify(noAvg, npab)>0) { - // Update - cbl.preLoop(); - update(cbl.inc(),es.getKey(),Notification.TYPE.OA); - } - } - } - } finally { - tt.done(); - } - trans.info().printf("Created %d Notifications", count.get()); - } + for(Entry es : mpending.entrySet()) { + p.row(napproveCW,es.getKey()); + ++cnt; + } + } finally { + tt.done(); + trans.info().printf("Processed %d Reminders", cnt); + } + } } - - private void update(StringBuilder sb, String user, TYPE oa) { - sb.append("UPDATE authz.notify SET last=dateof(now()) WHERE user='"); - sb.append(user); - sb.append("' AND type="); - sb.append(oa.idx()); - sb.append(';'); - - } @Override protected void _close(AuthzTrans trans) { - if(session!=null) { - session.close(); - session = null; - } + if(napproveCW!=null) { + napproveCW.flush(); + napproveCW.close(); + } } } diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Extend.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Extend.java index 870dc1e5..3a0f7b9e 100644 --- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Extend.java +++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Extend.java @@ -151,7 +151,7 @@ public class Extend extends Batch { gc.setTime(now.getTime()); } gc.add(gcType, extendBy); - UserRole.batchExtend(sb,row,Chrono.dateTime(gc)); + UserRole.batchExtend(sb,row,gc.getTime()); break; case "cred": int ctype = Integer.parseInt(row.get(3)); diff --git a/auth/auth-batch/src/test/java/org/onap/aaf/auth/batch/test/JU_NotificationTest.java b/auth/auth-batch/src/test/java/org/onap/aaf/auth/batch/test/JU_NotificationTest.java deleted file mode 100644 index f1cba0b7..00000000 --- a/auth/auth-batch/src/test/java/org/onap/aaf/auth/batch/test/JU_NotificationTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * ============LICENSE_START==================================================== - * org.onap.aaf - * =========================================================================== - * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. - * =========================================================================== - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ============LICENSE_END==================================================== - * - */ -package org.onap.aaf.auth.batch.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; -import static org.mockito.MockitoAnnotations.initMocks; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.onap.aaf.auth.batch.actions.Message; -import org.onap.aaf.auth.batch.helpers.Creator; -import org.onap.aaf.auth.batch.helpers.Notification; -import org.onap.aaf.auth.batch.helpers.Notification.TYPE; -import org.onap.aaf.auth.env.AuthzTrans; -import org.onap.aaf.misc.env.Env; -import org.onap.aaf.misc.env.LogTarget; -import org.onap.aaf.misc.env.TimeTaken; - -public class JU_NotificationTest { - - @Mock - private AuthzTrans trans; - @Mock - private Creator creator; - @Mock - private TimeTaken tt; - - @Mock - private LogTarget logTarget; - private Message msg; - - @Before - public void setUp() throws Exception { - initMocks(this); - - msg = new Message(); - msg.line("%n", "Message"); - - when(trans.info()).thenReturn(logTarget); - when(trans.start("Load Notify", Env.REMOTE)).thenReturn(tt); - } - - @Test - public void test() { - Notification notification = Notification.create("user", TYPE.CN); - assertEquals(notification.checksum(), 0); - notification.set(msg); - assertEquals(notification.checksum(), 10); - assertNull(Notification.get("user", TYPE.CN)); - assertTrue(notification.update(trans, null, true)); - assertTrue(notification.toString().contains("\"user\",\"CN\",")); - - - } -} \ No newline at end of file diff --git a/auth/auth-cass/cass_init/init.cql b/auth/auth-cass/cass_init/init.cql index 75b02c53..f280de00 100644 --- a/auth/auth-cass/cass_init/init.cql +++ b/auth/auth-cass/cass_init/init.cql @@ -90,12 +90,12 @@ CREATE TABLE cert ( CREATE INDEX cert_id ON cert(id); CREATE INDEX cert_x500 ON cert(x500); -CREATE TABLE notify ( +CREATE TABLE notified ( user text, - type int, + target text, + key text, last timestamp, - checksum int, - PRIMARY KEY (user,type) + PRIMARY KEY (user,target,key) ); CREATE TABLE x509 ( @@ -175,6 +175,7 @@ CREATE TABLE future ( ); CREATE INDEX future_idx ON future(target); CREATE INDEX future_start_idx ON future(start); +CREATE INDEX future_target_key ON authz.future (target_key); CREATE TABLE approval ( diff --git a/auth/auth-cass/cass_init/init2_10.cql b/auth/auth-cass/cass_init/init2_10.cql index b7195076..21bd4b63 100644 --- a/auth/auth-cass/cass_init/init2_10.cql +++ b/auth/auth-cass/cass_init/init2_10.cql @@ -2,3 +2,13 @@ use authz; alter TABLE cred ADD tag varchar; alter TABLE future ADD target_key varchar; alter TABLE future ADD target_date timestamp; +CREATE INDEX future_target_key ON authz.future (target_key); + +CREATE TABLE notified ( + user text, + target text, + key text, + last timestamp, + PRIMARY KEY (user,target,key) +); + diff --git a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java index 08c96853..c51ddbde 100644 --- a/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java +++ b/auth/auth-certman/src/main/java/org/onap/aaf/auth/cm/ca/LocalCA.java @@ -203,7 +203,7 @@ public class LocalCA extends CA { public X509andChain sign(Trans trans, CSRMeta csrmeta) throws IOException, CertException { GregorianCalendar gc = new GregorianCalendar(); Date start = gc.getTime(); - gc.add(GregorianCalendar.MONTH, 6); + gc.add(GregorianCalendar.MONTH, 12); Date end = gc.getTime(); X509Certificate x509; TimeTaken tt = trans.start("Create/Sign Cert",Env.SUB); diff --git a/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/pages/ApprovalForm.java b/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/pages/ApprovalForm.java index f1730380..0c984e4d 100644 --- a/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/pages/ApprovalForm.java +++ b/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/pages/ApprovalForm.java @@ -40,13 +40,12 @@ import org.onap.aaf.auth.gui.Table.Cells; import org.onap.aaf.auth.gui.table.AbsCell; import org.onap.aaf.auth.gui.table.ButtonCell; import org.onap.aaf.auth.gui.table.RadioCell; -import org.onap.aaf.auth.gui.table.RefCell; import org.onap.aaf.auth.gui.table.TableData; -import org.onap.aaf.auth.gui.table.TextAndRefCell; import org.onap.aaf.auth.gui.table.TextCell; +import org.onap.aaf.auth.gui.table.TextToolTipCell; import org.onap.aaf.auth.org.Organization; -import org.onap.aaf.auth.org.OrganizationFactory; import org.onap.aaf.auth.org.Organization.Identity; +import org.onap.aaf.auth.org.OrganizationFactory; import org.onap.aaf.cadi.CadiException; import org.onap.aaf.cadi.client.Future; import org.onap.aaf.cadi.client.Rcli; @@ -91,7 +90,7 @@ public class ApprovalForm extends Page { } }, new Form(true,new Table("Approval Requests", gui.env.newTransNoAvg(),new Model(gui.env),"class=stdform")) - .preamble("The following requires your Approval to proceed in the AAF System.

    Hover on Identity for Name; click for WebPhone; If Deny is the only option, User is no longer valid."), + .preamble("The following requires your Approval to proceed in the AAF System.

    Hover on Name for Identity; If Deny is the only option, User is no longer valid."), new NamedCode(false, "selectAlljs") { @Override public void code(final Cache cache, final HTMLGen hgen) throws APIException, IOException { @@ -116,7 +115,7 @@ public class ApprovalForm extends Page { */ private static class Model extends TableData { //TODO come up with a generic way to do ILM Info (people page) - private static final String TODO_ILM_INFO = "TODO: ILM Info"; +// private static final String TODO_ILM_INFO = "TODO: ILM Info"; private static final String[] headers = new String[] {"Identity","Request","Approve","Deny"}; @@ -216,7 +215,7 @@ public class ApprovalForm extends Page { // } else { approverHeader = new AbsCell[] { new TextCell("Approvals Delegated to Me by " + iapprover.fullName() - + '(' + iapprover.id() +')', + + '(' + iapprover.id() + ')', new String[] {"colspan=4", "class=head"}) }; // } @@ -242,34 +241,29 @@ public class ApprovalForm extends Page { userCell = AbsCell.Null; } else if (user.endsWith(trans.org().getRealm())){ userOK=true; -// String title; + String title; Organization org = OrganizationFactory.obtain(trans.env(), user); if (org==null) { -// title=""; + title=""; userCell = new TextCell(user); } else { Identity au = org.getIdentity(trans, user); if (au!=null) { if(au.isPerson()) { - userCell = new TextCell(au.fullName() + "\n(" + au.id() + ')'); + userCell = new TextToolTipCell(au.fullName(),"Identity: " + au.id()); } else { - userCell = new TextCell(au.fullID()); + Identity managedBy = au.responsibleTo(); + if (managedBy==null) { + title ="Identity: " + au.type(); + } else { + title="Sponsor: " + managedBy.fullName(); + } + userCell = new TextToolTipCell(au.fullID(),title); } -// -// if ("MECHID".equals(au.type())) { -// Identity managedBy = au.responsibleTo(); -// if (managedBy==null) { -// title ="title=" + au.type(); -// } else { -// title="title=Sponsor is " + managedBy.fullName(); -// } -// } else { -// title="title=" + au.fullName(); -// } } else { userOK=false; -// title="title=Not a User at " + org.getName(); - userCell = new TextCell(user); + title="Not a User at " + org.getName(); + userCell = new TextToolTipCell(user,title); } } prevUser=user; diff --git a/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/table/TextToolTipCell.java b/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/table/TextToolTipCell.java new file mode 100644 index 00000000..986b8245 --- /dev/null +++ b/auth/auth-gui/src/main/java/org/onap/aaf/auth/gui/table/TextToolTipCell.java @@ -0,0 +1,51 @@ +/** + * ============LICENSE_START==================================================== + * org.onap.aaf + * =========================================================================== + * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. + * =========================================================================== + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END==================================================== + * + */ + +package org.onap.aaf.auth.gui.table; + +import org.onap.aaf.misc.xgen.html.HTMLGen; + +/** + * Write Simple Text into a Cell + * @author Jonathan + * + */ +public class TextToolTipCell extends AbsCell { + public final String name; + private final String[] attrs; + private final String tooltip; + + public TextToolTipCell(String name, String tooltip, String... attributes) { + attrs = attributes; + this.name = name; + this.tooltip = ""; + } + + @Override + public void write(HTMLGen hgen) { + hgen.text(tooltip + name + ""); + } + + @Override + public String[] attrs() { + return attrs; + } +} diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java index cd8eff44..01bf6f2e 100644 --- a/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/filter/CadiFilter.java @@ -303,7 +303,14 @@ public class CadiFilter implements Filter { private boolean noAuthn(HttpServletRequest hreq) { if (pathExceptions!=null) { String pi = hreq.getPathInfo(); - if (pi==null) return false; // JBoss sometimes leaves null + if (pi==null) { + // Attempt to get from URI only (Daniel Rose) + pi = hreq.getRequestURI().substring(hreq.getContextPath().length()); + if(pi==null) { + // Nothing works. + return false; // JBoss sometimes leaves null + } + } for (String pe : pathExceptions) { if (pi.startsWith(pe))return true; } diff --git a/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java b/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java index 1d60ae58..47de84ed 100644 --- a/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java +++ b/cadi/core/src/main/java/org/onap/aaf/cadi/util/CSV.java @@ -44,6 +44,7 @@ public class CSV { private File csv; private Access access; private boolean processAll; + private char delimiter = ','; public CSV(Access access, File file) { this.access = access; @@ -57,6 +58,11 @@ public class CSV { processAll = false; } + public CSV setDelimiter(char delimiter) { + this.delimiter = delimiter; + return this; + } + public String name() { return csv.getName(); } @@ -116,16 +122,17 @@ public class CSV { escape = true; } break; - case ',': - if(quotes) { - sb.append(c); + default: + if(delimiter==c) { + if(quotes) { + sb.append(c); + } else { + row.add(sb.toString()); + sb.setLength(0); + } } else { - row.add(sb.toString()); - sb.setLength(0); + sb.append(c); } - break; - default: - sb.append(c); } } if(sb.length()>0 || c==',') { -- 2.16.6