summaryrefslogtreecommitdiffstats
path: root/auth/auth-batch/src/main
diff options
context:
space:
mode:
authorInstrumental <jonathan.gathman@att.com>2019-03-07 12:58:39 -0600
committerInstrumental <jonathan.gathman@att.com>2019-03-07 12:58:54 -0600
commita174f8ddbc5eb78a648fb68b33ef18cb64d81fda (patch)
treed27c01bcd63fd6a7bff00687c6f33423a3a40e8c /auth/auth-batch/src/main
parent9dc1cd2632d1dda9c8f072e99616e73e0517f1aa (diff)
Improve Batches
Issue-ID: AAF-740 Change-Id: Ib3e8a3f977964eed2e992dc02154dd3bc90492df Signed-off-by: Instrumental <jonathan.gathman@att.com>
Diffstat (limited to 'auth/auth-batch/src/main')
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/Pending.java1
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/approvalsets/URApprovalSet.java26
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Approval.java56
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/BatchDataView.java16
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java90
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Analyze.java4
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/Notify.java250
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyBody.java12
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyCredBody.java3
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyPendingApprBody.java69
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java37
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/temp/DataMigrateDublin.java215
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Approvals.java51
-rw-r--r--auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Remove.java2
14 files changed, 612 insertions, 220 deletions
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 2e7997b4..3072038a 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
@@ -105,4 +105,5 @@ public class Pending {
public boolean newApprovals() {
return hasNew;
}
+
} \ 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 858690ac..2c1ffe6d 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
@@ -21,6 +21,7 @@
package org.onap.aaf.auth.batch.approvalsets;
import java.io.IOException;
+import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
@@ -40,7 +41,7 @@ import org.onap.aaf.misc.env.util.Chrono;
public class URApprovalSet extends ApprovalSet {
- private boolean ownerSuperApprove;
+ private boolean ownerSuperApprove = true;
public URApprovalSet(final AuthzTrans trans, final GregorianCalendar start, final DataView dv, final Loader<UserRoleDAO.Data> lurdd) throws IOException, CadiException {
super(start, "user_role", dv);
@@ -49,6 +50,8 @@ public class URApprovalSet extends ApprovalSet {
setConstruct(urdd.bytify());
setMemo(getMemo(urdd));
setExpires(org.expiration(null, Organization.Expiration.UserInRole));
+ setTargetKey(urdd.role);
+ setTargetDate(urdd.expires);
Result<RoleDAO.Data> r = dv.roleByName(trans, urdd.role);
if(r.notOKorIsEmpty()) {
@@ -88,18 +91,13 @@ public class URApprovalSet extends ApprovalSet {
}
}
- if(isOwner && ownerSuperApprove) {
+ if(isOwner) {
try {
List<Identity> apprs = org.getApprovers(trans, urdd.user);
if(apprs!=null) {
for(Identity i : apprs) {
ApprovalDAO.Data add = newApproval(urdd);
- Identity reportsTo = i.responsibleTo();
- if(reportsTo!=null) {
- add.approver = reportsTo.fullID();
- } else {
- throw new CadiException("No Supervisor for '" + urdd.user + '\'');
- }
+ add.approver = i.fullID();
add.type = org.getApproverType();
ladd.add(add);
}
@@ -110,8 +108,16 @@ public class URApprovalSet extends ApprovalSet {
}
}
- public void ownerSuperApprove() {
- ownerSuperApprove = true;
+ private void setTargetDate(Date expires) {
+ fdd.target_date = expires;
+ }
+
+ private void setTargetKey(String key) {
+ fdd.target_key = key;
+ }
+
+ public void ownerSuperApprove(boolean set) {
+ ownerSuperApprove = set;
}
private ApprovalDAO.Data newApproval(UserRoleDAO.Data urdd) throws CadiException {
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Approval.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Approval.java
index 2cc6907b..1bc82f5e 100644
--- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Approval.java
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/Approval.java
@@ -57,13 +57,13 @@ public class Approval implements CacheChange.Data {
public final ApprovalDAO.Data add;
private String role;
- public Approval(UUID id, UUID ticket, String approver, Date last_notified,
+ public Approval(UUID id, UUID ticket, String approver,// Date last_notified,
String user, String memo, String operation, String status, String type, long updated) {
add = new ApprovalDAO.Data();
add.id = id;
add.ticket = ticket;
add.approver = approver;
- add.last_notified = last_notified;
+// add.last_notified = last_notified;
add.user = user;
add.memo = memo;
add.operation = operation;
@@ -125,8 +125,8 @@ public class Approval implements CacheChange.Data {
}
}
- public static void row(CSV.Writer cw, Approval app) {
- cw.row("approval",app.add.id,app.add.ticket,app.add.user,app.role,app.add.memo);
+ public static void row(CSV.RowSetter crs, Approval app) {
+ crs.row("approval",app.add.id,app.add.ticket,app.add.user,app.role,app.add.memo);
}
@@ -211,41 +211,41 @@ public class Approval implements CacheChange.Data {
}
}
- public void update(AuthzTrans trans, ApprovalDAO apprDAO, boolean dryRun) {
- if (dryRun) {
- trans.info().printf("Would update Approval %s, %s, last_notified %s",add.id,add.status,add.last_notified);
- } else {
- trans.info().printf("Update Approval %s, %s, last_notified %s",add.id,add.status,add.last_notified);
- apprDAO.update(trans, add);
- }
- }
+// public void update(AuthzTrans trans, ApprovalDAO apprDAO, boolean dryRun) {
+// if (dryRun) {
+// trans.info().printf("Would update Approval %s, %s, last_notified %s",add.id,add.status,add.last_notified);
+// } else {
+// trans.info().printf("Update Approval %s, %s, last_notified %s",add.id,add.status,add.last_notified);
+// apprDAO.update(trans, add);
+// }
+// }
public static Creator<Approval> v2_0_17 = new Creator<Approval>() {
@Override
public Approval create(Row row) {
- return new Approval(row.getUUID(0), row.getUUID(1), row.getString(2), row.getTimestamp(3),
- row.getString(4),row.getString(5),row.getString(6),row.getString(7),row.getString(8)
- ,row.getLong(9)/1000);
+ return new Approval(row.getUUID(0), row.getUUID(1), row.getString(2),
+ row.getString(3),row.getString(4),row.getString(5),row.getString(6),row.getString(7),
+ row.getLong(8)/1000);
}
@Override
public String select() {
- return "select id,ticket,approver,last_notified,user,memo,operation,status,type,WRITETIME(status) from authz.approval";
+ return "select id,ticket,approver,user,memo,operation,status,type,WRITETIME(status) from authz.approval";
}
};
- /**
- * @return the lastNotified
- */
- public Date getLast_notified() {
- return add.last_notified;
- }
- /**
- * @param lastNotified the lastNotified to set
- */
- public void setLastNotified(Date last_notified) {
- add.last_notified = last_notified;
- }
+// /**
+// * @return the lastNotified
+// */
+// public Date getLast_notified() {
+// return add.last_notified;
+// }
+// /**
+// * @param lastNotified the lastNotified to set
+// */
+// public void setLastNotified(Date last_notified) {
+// add.last_notified = last_notified;
+// }
/**
* @return the status
*/
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/BatchDataView.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/BatchDataView.java
index 37def6d6..83945ee6 100644
--- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/BatchDataView.java
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/BatchDataView.java
@@ -134,12 +134,12 @@ public class BatchDataView implements DataView {
public Result<ApprovalDAO.Data> insert(AuthzTrans trans, ApprovalDAO.Data add) {
cqlBatch.preLoop();
StringBuilder sb = cqlBatch.inc();
- sb.append("INSERT INTO authz.approval (id,approver,last_notified,memo,operation,status,ticket,type,user) VALUES (");
+ sb.append("INSERT INTO authz.approval (id,approver,memo,operation,status,ticket,type,user) VALUES (");
sb.append(add.id.toString());
sb.append(COMMA_QUOTE);
sb.append(add.approver);
- sb.append(QUOTE_COMMA_QUOTE);
- sb.append(Chrono.utcStamp(add.last_notified));
+// sb.append(QUOTE_COMMA_QUOTE);
+// sb.append(Chrono.utcStamp(add.last_notified));
sb.append(QUOTE_COMMA_QUOTE);
sb.append(add.memo.replace("'", "''"));
sb.append(QUOTE_COMMA_QUOTE);
@@ -160,7 +160,7 @@ public class BatchDataView implements DataView {
public Result<FutureDAO.Data> insert(AuthzTrans trans, FutureDAO.Data fdd) {
cqlBatch.preLoop();
StringBuilder sb = cqlBatch.inc();
- sb.append("INSERT INTO authz.future (id,construct,expires,memo,start,target) VALUES (");
+ sb.append("INSERT INTO authz.future (id,construct,expires,memo,start,target,target_key,target_date) VALUES (");
sb.append(fdd.id.toString());
sb.append(',');
fdd.construct.hasArray();
@@ -173,6 +173,14 @@ public class BatchDataView implements DataView {
sb.append(Chrono.utcStamp(fdd.expires));
sb.append(QUOTE_COMMA_QUOTE);
sb.append(fdd.target);
+ if(fdd.target_key==null) {
+ sb.append("',,'");
+ } else {
+ sb.append(QUOTE_COMMA_QUOTE);
+ sb.append(fdd.target_key==null?"":fdd.target_key);
+ sb.append(QUOTE_COMMA_QUOTE);
+ }
+ sb.append(Chrono.utcStamp(fdd.target_date));
sb.append(QUOTE_PAREN_SEMI);
return Result.ok(fdd);
}
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
new file mode 100644
index 00000000..22231f3a
--- /dev/null
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/helpers/LastNotified.java
@@ -0,0 +1,90 @@
+/**
+ * ============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.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.onap.aaf.auth.batch.helpers.Notification.TYPE;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+import com.datastax.driver.core.Session;
+
+public class LastNotified {
+ private Map<String,Date> lastNotified = new TreeMap<>();
+ private Session session;
+
+ public LastNotified(Session session) {
+ this.session = session;
+ }
+
+ public void add(Set<String> users) {
+ StringBuilder query = new StringBuilder();
+ startNotifyQuery(query);
+ int cnt = 0;
+ for(String user : users) {
+ if(++cnt>1) {
+ query.append(',');
+ }
+ query.append('\'');
+ query.append(user);
+ query.append('\'');
+ if(cnt>=30) {
+ endNotifyQuery(query, Notification.TYPE.OA);
+ add(session.execute(query.toString()),lastNotified);
+ query.setLength(0);
+ startNotifyQuery(query);
+ cnt=0;
+ }
+ }
+ if(cnt>0) {
+ endNotifyQuery(query, Notification.TYPE.OA);
+ add(session.execute(query.toString()),lastNotified);
+ }
+ }
+
+ public Date lastNotified(String user) {
+ return lastNotified.get(user);
+ }
+
+ private void add(ResultSet result, Map<String, Date> lastNotified) {
+ for(Iterator<Row> iter = result.iterator(); iter.hasNext();) {
+ Row r = iter.next();
+ lastNotified.put(r.getString(0), r.getTimestamp(1));
+ }
+ }
+
+ private void startNotifyQuery(StringBuilder query) {
+ query.append("SELECT user,last FROM authz.notify WHERE user in (");
+ }
+
+ private void endNotifyQuery(StringBuilder query, TYPE oa) {
+ query.append(") AND type=");
+ query.append(oa.idx());
+ query.append(';');
+ }
+}
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 35020836..a0dce749 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
@@ -231,7 +231,7 @@ public class Analyze extends Batch {
++state[type][pending];
Pending n = pendingTemp.get(appr.getApprover());
if(n==null) {
- pendingTemp.put(appr.getApprover(),new Pending(appr.getLast_notified()));
+ pendingTemp.put(appr.getApprover(),new Pending());
} else {
n.inc();
}
@@ -288,7 +288,7 @@ public class Analyze extends Batch {
for(Entry<String, Pending> es : pendingApprs.entrySet()) {
Pending p = es.getValue();
- if(p.earliest() == null || p.earliest().after(remind)) {
+ if(p.newApprovals() || p.earliest() == null || p.earliest().after(remind)) {
p.row(approveCW,es.getKey());
}
}
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 0524c5cb..1c1f660c 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
@@ -20,38 +20,38 @@
*/package org.onap.aaf.auth.batch.reports;
import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationTargetException;
- import java.util.ArrayList;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
- import org.onap.aaf.auth.batch.Batch;
- import org.onap.aaf.auth.batch.reports.bodies.NotifyBody;
- 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.client.Holder;
- import org.onap.aaf.cadi.util.CSV;
- import org.onap.aaf.misc.env.APIException;
- import org.onap.aaf.misc.env.util.Chrono;
+import org.onap.aaf.auth.batch.Batch;
+import org.onap.aaf.auth.batch.reports.bodies.NotifyBody;
+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.client.Holder;
+import org.onap.aaf.cadi.util.CSV;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.util.Chrono;
public class Notify extends Batch {
private static final String HTML_CSS = "HTML_CSS";
private final Mailer mailer;
private final String header;
private final String footer;
- private Set<File> notifyFile;
+ private final int maxEmails;
+ private final int indent;
+ private final boolean urgent;
public final String guiURL;
- private int maxEmails;
- private int indent;
public Notify(AuthzTrans trans) throws APIException, IOException, OrganizationException {
super(trans.env());
@@ -59,9 +59,9 @@
String mailFrom = env.getProperty("MAIL_FROM");
String header_html = env.getProperty("HEADER_HTML");
String footer_html = env.getProperty("FOOTER_HTML");
- String maxEmails = env.getProperty("MAX_EMAIL");
+ String str = env.getProperty("MAX_EMAIL");
guiURL = env.getProperty("GUI_URL");
- this.maxEmails = maxEmails==null?1:Integer.parseInt(maxEmails);
+ maxEmails = str==null||str.isEmpty()?Integer.MAX_VALUE:Integer.parseInt(str);
if(mailerCls==null || mailFrom==null || guiURL==null || header_html==null || footer_html==null) {
throw new APIException("Notify requires MAILER, MAILER_FROM, GUI_URL, HEADER_HTML and FOOTER_HTML properties");
}
@@ -101,9 +101,12 @@
} else {
indent = 6; //arbitrary
}
+ } else {
+ indent = 6;
}
-
+ urgent = false;
+
sb.setLength(0);
br = new BufferedReader(new FileReader(footer_html));
try {
@@ -116,46 +119,45 @@
br.close();
}
- // Class Load possible data
- NotifyBody.load(env.access());
-
- // Create Intermediate Output
- File logDir = logDir();
- notifyFile = new HashSet<>();
- if(args().length>0) {
- for(int i=0;i<args().length;++i) {
- 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()));
- if(file.exists()) {
- trans.info().printf("Processing '%s' in %s",nb.type(),file.getCanonicalPath());
- notifyFile.add(file);
- } else {
- trans.info().printf("No Files found for %s",nb.name());
- }
- }
- }
}
+ /*
+ * Note: We try to put things related to Notify as Main Class in Run, where we might have put in
+ * Constructor, so that we can have other Classes call just the "notify" method.
+ */
@Override
protected void run(AuthzTrans trans) {
- List<String> toList = new ArrayList<>();
- List<String> ccList = new ArrayList<>();
AuthzTrans noAvg = trans.env().newTransNoAvg();
- String subject = "Test Notify";
- boolean urgent = false;
-
-
- final Notify notify = this;
final Holder<List<String>> info = new Holder<>(null);
final Set<String> errorSet = new HashSet<>();
try {
+ // Class Load possible data
+ NotifyBody.load(env.access());
+
+
+ // Create Intermediate Output
+ File logDir = logDir();
+ Set<File> notifyFile = new HashSet<>();
+ if(args().length>0) {
+ for(int i=0;i<args().length;++i) {
+ 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()));
+ if(file.exists()) {
+ trans.info().printf("Processing '%s' in %s",nb.type(),file.getCanonicalPath());
+ notifyFile.add(file);
+ } else {
+ trans.info().printf("No Files found for %s",nb.name());
+ }
+ }
+ }
+
for(File f : notifyFile) {
CSV csv = new CSV(env.access(),f);
try {
@@ -167,8 +169,7 @@
}
if(info.get()==null) {
throw new CadiException("First line of Feed MUST contain 'info' record");
- }
- String key = row.get(0)+'|'+info.get().get(1);
+ } String key = row.get(0)+'|'+info.get().get(1);
NotifyBody body = NotifyBody.get(key);
if(body==null) {
errorSet.add("No NotifyBody defined for " + key);
@@ -185,78 +186,93 @@
// now create Notification
for(NotifyBody nb : NotifyBody.getAll()) {
- String run = nb.type()+nb.name();
- String test = dryRun?run:null;
- ONE_EMAIL:
- for(String id : nb.users()) {
+ notify(noAvg, nb);
+ }
- toList.clear();
- ccList.clear();
- try {
- Identity identity = trans.org().getIdentity(noAvg, id);
- if(identity==null) {
- trans.warn().printf("%s is invalid for this Organization. Skipping notification.",id);
- } else {
- if(!identity.isPerson()) {
- identity = identity.responsibleTo();
- }
- if(identity==null) {
- trans.warn().printf("Responsible Identity %s is invalid for this Organization. Skipping notification.",id);
- } else {
- for(int i=1;i<=nb.escalation();++i) {
- if(identity != null) {
- if(i==1) {
- toList.add(identity.email());
- List<String> dels = identity.delegate();
- if(dels!=null) {
- for(String d : dels) {
- toList.add(d);
- }
- }
- } else {
- Identity s = identity.responsibleTo();
- if(s==null) {
- trans.error().printf("Identity %s has no %s", identity.fullID(),
- identity.isPerson()?"supervisor":"sponsor");
- } else {
- ccList.add(s.email());
- }
- }
+ } catch (APIException | IOException e1) {
+ trans.error().log(e1);
+ } finally {
+ for(String s : errorSet) {
+ trans.audit().log(s);
+ }
+ }
+ }
+
+ public int notify(AuthzTrans trans, NotifyBody nb) {
+ List<String> toList = new ArrayList<>();
+ List<String> ccList = new ArrayList<>();
+
+ String run = nb.type()+nb.name();
+ String test = dryRun?run:null;
+ String last = null;
+
+ ONE_EMAIL:
+ for(String id : nb.users()) {
+ last = id;
+ toList.clear();
+ ccList.clear();
+ try {
+ Identity identity = trans.org().getIdentity(trans, id);
+ if(identity==null) {
+ trans.warn().printf("%s is invalid for this Organization. Skipping notification.",id);
+ } else {
+ if(!identity.isPerson()) {
+ identity = identity.responsibleTo();
+ }
+ if(identity==null) {
+ trans.warn().printf("Responsible Identity %s is invalid for this Organization. Skipping notification.",id);
+ } else {
+ for(int i=1;i<=nb.escalation();++i) {
+ if(identity != null) {
+ if(i==1) { // self and Delegates
+ toList.add(identity.email());
+ List<String> dels = identity.delegate();
+ if(dels!=null) {
+ for(String d : dels) {
+ toList.add(d);
}
}
-
- StringBuilder content = new StringBuilder();
- content.append(String.format(header,version,Identity.mixedCase(identity.firstName())));
-
- nb.body(noAvg, content, indent, notify, id);
- content.append(footer);
-
- if(mailer.sendEmail(noAvg, test, toList, ccList, nb.subject(),content.toString(), urgent)) {
- nb.inc();
+ } else {
+ Identity s = identity.responsibleTo();
+ if(s==null) {
+ trans.error().printf("Identity %s has no %s", identity.fullID(),
+ identity.isPerson()?"supervisor":"sponsor");
} else {
- trans.error().log("Mailer failed to send Mail");
- }
- if(maxEmails>0 && nb.count()>=maxEmails) {
- break ONE_EMAIL;
+ ccList.add(s.email());
}
}
}
- } catch (OrganizationException e) {
- trans.error().log(e);
}
- }
- trans.info().printf("Emailed %d for %s",nb.count(),run);
- }
+ StringBuilder content = new StringBuilder();
+ content.append(String.format(header,version,Identity.mixedCase(identity.firstName())));
- } finally {
- for(String s : errorSet) {
- trans.audit().log(s);
+ nb.body(trans, content, indent, this, id);
+ content.append(footer);
+
+ if(mailer.sendEmail(trans, test, toList, ccList, nb.subject(),content.toString(), urgent)) {
+ nb.inc();
+ } else {
+ trans.error().log("Mailer failed to send Mail");
+ }
+ if(maxEmails>0 && nb.count()>=maxEmails) {
+ break ONE_EMAIL;
+ }
+ }
+ }
+ } catch (OrganizationException e) {
+ trans.error().log(e);
}
}
+ if(nb.count()<=1) {
+ trans.info().printf("Notified %s for %s",last,run);
+ } else {
+ trans.info().printf("Emailed %d for %s",nb.count(),run);
+ }
+ return nb.count();
}
- @Override
+ @Override
protected void _close(AuthzTrans trans) {
}
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyBody.java
index 034286cf..bf20eb41 100644
--- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyBody.java
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyBody.java
@@ -195,16 +195,24 @@ public abstract class NotifyBody {
}
}
- protected void println(StringBuilder sb, int indent, Object ... objs) {
+ protected void print(StringBuilder sb, int indent, Object ... objs) {
for(int i=0;i<indent;++i) {
sb.append(' ');
}
for(Object o : objs) {
sb.append(o.toString());
}
+ }
+
+ protected void println(StringBuilder sb, int indent, Object ... objs) {
+ print(sb,indent,objs);
sb.append('\n');
}
-
+
+ protected void printf(StringBuilder sb, int indent, String fmt, Object ... objs) {
+ print(sb,indent,String.format(fmt, objs));
+ }
+
protected String printCell(StringBuilder sb, int indent, String current, String prev) {
if(current.equals(prev)) {
println(sb,indent,DUPL);
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 deac3e41..94502d9f 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
@@ -36,7 +36,8 @@ public abstract class NotifyCredBody extends NotifyBody {
super(access,"cred",name);
// Default
- explanation = "The following Credentials are expiring on the dates shown. "
+ 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."
+ "<h3>Instructions for 'Password':</h3><ul>"
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyPendingApprBody.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyPendingApprBody.java
index 502464e9..df28503c 100644
--- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyPendingApprBody.java
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/NotifyPendingApprBody.java
@@ -27,64 +27,27 @@ import org.onap.aaf.auth.env.AuthzTrans;
import org.onap.aaf.cadi.Access;
public class NotifyPendingApprBody extends NotifyBody {
- private final String explanation;
public NotifyPendingApprBody(Access access) {
super(access,"appr","PendingApproval");
- explanation = "The following Approvals are awaiting your action. ";
}
@Override
public boolean body(AuthzTrans trans, StringBuilder sb, int indent, Notify n, String id) {
- println(sb,indent,explanation);
-/* println(sb,indent,"<table>");
- indent+=2;
- println(sb,indent,"<tr>");
- indent+=2;
- println(sb,indent,"<th>Fully Qualified ID</th>");
- println(sb,indent,"<th>Unique ID</th>");
- println(sb,indent,"<th>Type</th>");
- println(sb,indent,"<th>Expires</th>");
- println(sb,indent,"<th>Warnings</th>");
- indent-=2;
- println(sb,indent,"</tr>");
- String theid, type, info, expires, warnings;
- GregorianCalendar gc = new GregorianCalendar();
+ boolean rv = false;
for(List<String> row : rows.get(id)) {
- theid=row.get(1);
- switch(row.get(3)) {
- case "1":
- case "2":
- type = "Password";
- break;
- case "200":
- type = "x509 (Certificate)";
- break;
- default:
- type = "Unknown, see AAF GUI";
- break;
+ String qty = row.get(2);
+ if("1".equals(qty)) {
+ printf(sb,indent,"You have an Approval in the AAF %s Environment awaiting your decision.\n",row.get(3));
+ } else {
+ printf(sb,indent,"You have %s Approvals in the AAF %s Environment awaiting your decision.\n",qty,row.get(3));
}
- theid = "<a href=\""+n.guiURL+"/creddetail?ns="+row.get(2)+"\">"+theid+"</a>";
- gc.setTimeInMillis(Long.parseLong(row.get(5)));
- expires = Chrono.niceUTCStamp(gc);
- info = row.get(6);
- //TODO get Warnings
- warnings = "";
-
- println(sb,indent,"<tr>");
- indent+=2;
- printCell(sb,indent,theid);
- printCell(sb,indent,info);
- printCell(sb,indent,type);
- printCell(sb,indent,expires);
- printCell(sb,indent,warnings);
- indent-=2;
- println(sb,indent,"</tr>");
+ printf(sb,indent,"<br><br><b>ACTION:</b> <i>Click on</i> <a href=\"%s/approve\">AAF Approval Page</a>",n.guiURL);
+ rv = true;
+ break; // only one
}
- indent-=2;
- println(sb,indent,"</table>");
- */
- return true;
+
+ return rv;
}
@Override
@@ -100,4 +63,14 @@ public class NotifyPendingApprBody extends NotifyBody {
return String.format("AAF Pending Approval Notification (ENV: %s)",env);
}
+ /* (non-Javadoc)
+ * @see org.onap.aaf.auth.batch.reports.bodies.NotifyBody#store(java.util.List)
+ */
+ @Override
+ public void store(List<String> row) {
+ // Notify Pending is setup for 1 Notification at a time
+ super.rows.clear();
+ super.store(row);
+ }
+
}
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
new file mode 100644
index 00000000..eb52c6d0
--- /dev/null
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/reports/bodies/TwoMonthNotifyCredBody.java
@@ -0,0 +1,37 @@
+/**
+ * ============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.reports.bodies;
+
+import java.io.IOException;
+
+import org.onap.aaf.auth.batch.helpers.ExpireRange;
+import org.onap.aaf.cadi.Access;
+
+public class TwoMonthNotifyCredBody extends NotifyCredBody {
+ public TwoMonthNotifyCredBody(Access access) throws IOException {
+ super(access, ExpireRange.TWO_MONTH);
+ }
+
+ @Override
+ public String subject() {
+ return String.format("AAF Two Month Credential Notification (ENV: %s)",env);
+ }
+}
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/temp/DataMigrateDublin.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/temp/DataMigrateDublin.java
new file mode 100644
index 00000000..4851662b
--- /dev/null
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/temp/DataMigrateDublin.java
@@ -0,0 +1,215 @@
+/**
+ * ============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.temp;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.security.SecureRandom;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.aaf.auth.batch.Batch;
+import org.onap.aaf.auth.batch.BatchPrincipal;
+import org.onap.aaf.auth.batch.helpers.CQLBatch;
+import org.onap.aaf.auth.batch.helpers.CQLBatchLoop;
+import org.onap.aaf.auth.dao.cass.CredDAO;
+import org.onap.aaf.auth.env.AuthzTrans;
+import org.onap.aaf.auth.org.OrganizationException;
+import org.onap.aaf.cadi.Hash;
+import org.onap.aaf.cadi.configure.Factory;
+import org.onap.aaf.misc.env.APIException;
+import org.onap.aaf.misc.env.Env;
+import org.onap.aaf.misc.env.TimeTaken;
+
+import com.datastax.driver.core.ResultSet;
+import com.datastax.driver.core.Row;
+
+public class DataMigrateDublin extends Batch {
+ private final SecureRandom sr;
+ private final AuthzTrans noAvg;
+
+ public DataMigrateDublin(AuthzTrans trans) throws APIException, IOException, OrganizationException {
+ super(trans.env());
+ trans.info().log("Starting Connection Process");
+
+ noAvg = env.newTransNoAvg();
+ noAvg.setUser(new BatchPrincipal("Migrate"));
+
+ TimeTaken tt0 = trans.start("Cassandra Initialization", Env.SUB);
+ try {
+ TimeTaken tt = trans.start("Connect to Cluster", Env.REMOTE);
+ try {
+ session = cluster.connect();
+ } finally {
+ tt.done();
+ }
+ } finally {
+ tt0.done();
+ }
+
+ sr = new SecureRandom();
+ }
+
+ @Override
+ protected void run(AuthzTrans trans) {
+ ///////////////////////////
+ trans.info().log("Add UniqueTag to Passwords");
+
+ CQLBatchLoop cbl = new CQLBatchLoop(new CQLBatch(noAvg.info(),session), 50, dryRun);
+ try {
+ ResultSet rs = session.execute("SELECT id,type,expires,cred,tag FROM authz.cred");
+ Iterator<Row> iter = rs.iterator();
+ Row row;
+ int count = 0;
+ byte[] babytes = new byte[6];
+ Map<String, List<CredInfo>> mlci = new TreeMap<>();
+ Map<String, String> ba_tag = new TreeMap<>();
+ while(iter.hasNext()) {
+ ++count;
+ row = iter.next();
+ String tag = row.getString(4);
+ int type = row.getInt(1);
+ switch(type) {
+ case CredDAO.BASIC_AUTH:
+ case CredDAO.BASIC_AUTH_SHA256:
+ String key = row.getString(0) + '|' + type + '|' + Hash.toHex(row.getBytesUnsafe(3).array());
+ String btag = ba_tag.get(key);
+ if(btag == null) {
+ if(tag==null || tag.isEmpty()) {
+ sr.nextBytes(babytes);
+ btag = Hash.toHexNo0x(babytes);
+ } else {
+ btag = tag;
+ }
+ ba_tag.put(key, btag);
+ }
+
+ if(!btag.equals(tag)) {
+ cbl.preLoop();
+ update(cbl,row,btag);
+ }
+ break;
+ case CredDAO.CERT_SHA256_RSA:
+ if(tag==null || tag.isEmpty()) {
+ String id = row.getString(0);
+ List<CredInfo> ld = mlci.get(id);
+ if(ld==null) {
+ ld = new ArrayList<>();
+ mlci.put(id,ld);
+ }
+ ld.add(new CredInfo(id,row.getInt(1),row.getTimestamp(2)));
+ }
+ break;
+ }
+ }
+ cbl.flush();
+ trans.info().printf("Processes %d cred records, updated %d records in %d batches.", count, cbl.total(), cbl.batches());
+ count = 0;
+
+ cbl.reset();
+
+ trans.info().log("Add Serial to X509 Creds");
+ rs = session.execute("SELECT ca, id, x509 FROM authz.x509");
+ iter = rs.iterator();
+ while(iter.hasNext()) {
+ ++count;
+ row = iter.next();
+ String ca = row.getString(0);
+ String id = row.getString(1);
+ List<CredInfo> list = mlci.get(id);
+ if(list!=null) {
+ ByteBuffer bb = row.getBytesUnsafe(2);
+ if(bb!=null) {
+ Collection<? extends Certificate> x509s = Factory.toX509Certificate(bb.array());
+ for(Certificate c : x509s) {
+ X509Certificate xc = (X509Certificate)c;
+ for(CredInfo ci : list) {
+ if(xc.getNotAfter().equals(ci.expires)) {
+ cbl.preLoop();
+ ci.update(cbl, ca + '|' + xc.getSerialNumber());
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ cbl.flush();
+ trans.info().printf("Processed %d x509 records, updated %d records in %d batches.", count, cbl.total(), cbl.batches());
+ count = 0;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static class CredInfo {
+ public final String id;
+ public final int type;
+ public final Date expires;
+
+ public CredInfo(String id, int type, Date expires) {
+ this.id = id;
+ this.type = type;
+ this.expires = expires;
+ }
+
+ public void update(CQLBatchLoop cbl, String newtag) {
+ StringBuilder sb = cbl.inc();
+ sb.append("UPDATE authz.cred SET tag='");
+ sb.append(newtag);
+ sb.append("' WHERE id='");
+ sb.append(id);
+ sb.append("' AND type=");
+ sb.append(type);
+ sb.append(" AND expires=dateof(maxtimeuuid(");
+ sb.append(expires.getTime());
+ sb.append("));");
+ }
+ }
+
+ private void update(CQLBatchLoop cbl, Row row, String newtag) {
+ StringBuilder sb = cbl.inc();
+ sb.append("UPDATE authz.cred SET tag='");
+ sb.append(newtag);
+ sb.append("' WHERE id='");
+ sb.append(row.getString(0));
+ sb.append("' AND type=");
+ sb.append(row.getInt(1));
+ sb.append(" AND expires=dateof(maxtimeuuid(");
+ Date lc = row.getTimestamp(2);
+ sb.append(lc.getTime());
+ sb.append("));");
+ }
+
+ @Override
+ protected void _close(AuthzTrans trans) {
+ trans.info().log("End " + this.getClass().getSimpleName() + " processing" );
+ session.close();
+ }
+
+}
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 2047098a..03c812ae 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
@@ -38,15 +38,21 @@ 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;
@@ -56,15 +62,18 @@ import org.onap.aaf.misc.env.Trans;
import org.onap.aaf.misc.env.util.Chrono;
public class Approvals extends Batch {
- private final AuthzTrans noAvg;
+ private final Access access;
+ private final AuthzTrans noAvg;
private BatchDataView dataview;
private List<CSV> csvList;
private GregorianCalendar now;
+ private final Notify notify;
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();
@@ -94,13 +103,13 @@ public class Approvals extends Batch {
trans.error().printf("CSV File %s does not exist",f.getAbsolutePath());
}
}
-
}
@Override
protected void run(AuthzTrans trans) {
Map<String,Pending> mpending = new TreeMap<>();
Holder<Integer> 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 {
@@ -170,19 +179,36 @@ public class Approvals extends Batch {
trans.info().printf("Processed %d UserRoles", count.get());
count.set(0);
- NotifyPendingApprBody npab;
+ 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);
try {
for(Entry<String, Pending> es : mpending.entrySet()) {
p = es.getValue();
- Date earliest = p.earliest();
- if(p.newApprovals() || earliest==null || earliest.before(oneWeek) ) {
- System.out.println("update");
+ 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 {
@@ -192,7 +218,16 @@ public class Approvals extends Batch {
}
}
- @Override
+ 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();
diff --git a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Remove.java b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Remove.java
index a0fdcc50..7a30dc5f 100644
--- a/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Remove.java
+++ b/auth/auth-batch/src/main/java/org/onap/aaf/auth/batch/update/Remove.java
@@ -138,6 +138,8 @@ public class Remove extends Batch {
if(!ur.get()) {
ur.set(true);
}
+ //TODO If deleted because Role is no longer there, double check...
+
UserRole.batchDelete(cbl.inc(),row);
hdd.target=UserRoleDAO.TABLE;
hdd.subject=UserRole.histSubject(row);