2 * ============LICENSE_START====================================================
4 * ===========================================================================
5 * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6 * ===========================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END====================================================
22 package org.onap.aaf.auth.update;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.PrintStream;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.Date;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Map.Entry;
36 import org.onap.aaf.auth.Batch;
37 import org.onap.aaf.auth.BatchPrincipal;
38 import org.onap.aaf.auth.actions.Email;
39 import org.onap.aaf.auth.actions.EmailPrint;
40 import org.onap.aaf.auth.actions.Message;
41 import org.onap.aaf.auth.dao.cass.CredDAO;
42 import org.onap.aaf.auth.dao.hl.Question;
43 import org.onap.aaf.auth.env.AuthzTrans;
44 import org.onap.aaf.auth.helpers.Cred;
45 import org.onap.aaf.auth.helpers.Notification;
46 import org.onap.aaf.auth.helpers.UserRole;
47 import org.onap.aaf.auth.helpers.Notification.TYPE;
48 import org.onap.aaf.auth.layer.Result;
49 import org.onap.aaf.auth.org.EmailWarnings;
50 import org.onap.aaf.auth.org.Organization;
51 import org.onap.aaf.auth.org.OrganizationException;
52 import org.onap.aaf.auth.org.OrganizationFactory;
53 import org.onap.aaf.auth.org.Organization.Identity;
54 import org.onap.aaf.misc.env.APIException;
55 import org.onap.aaf.misc.env.Env;
56 import org.onap.aaf.misc.env.TimeTaken;
57 import org.onap.aaf.misc.env.util.Chrono;
59 import java.util.TreeMap;
62 public class NotifyCredExpiring extends Batch {
64 private static final String UNKNOWN_ID = "unknown@deprecated.id";
65 private static final String AAF_INSTANTIATED_MECHID = "AAF INSTANTIATED MECHID";
66 private static final String EXPIRATION_DATE = "EXPIRATION DATE";
67 private static final String QUICK_LINK = "QUICK LINK TO UPDATE PAGE";
68 private static final String DASH_1 = "-----------------------";
69 private static final String DASH_2 = "---------------";
70 private static final String DASH_3 = "----------------------------------------------------";
71 private static final String LINE = "\n----------------------------------------------------------------";
73 private int maxEmails;
74 private final PrintStream ps;
75 private final AuthzTrans noAvg;
76 private String supportEmailAddr;
78 public NotifyCredExpiring(AuthzTrans trans) throws APIException, IOException, OrganizationException {
80 TimeTaken tt = trans.start("Connect to Cluster", Env.REMOTE);
82 session = cluster.connect();
87 noAvg = env.newTransNoAvg();
88 noAvg.setUser(new BatchPrincipal("batch:NotifyCredExpiring"));
90 if ((supportEmailAddr = env.getProperty("mailFromUserId"))==null) {
91 throw new APIException("mailFromUserId property must be set");
94 email = new EmailPrint();
96 maxEmails = Integer.parseInt(trans.getProperty("MAX_EMAILS","3"));
99 maxEmails = Integer.parseInt(trans.getProperty("MAX_EMAILS","3"));
102 email.subject("AAF Password Expiration Notification (ENV: %s)",batchEnv);
103 email.preamble("AAF (MOTS 22830) is the AT&T Authorization System used by many AT&T Tools and Applications.\n\n" +
104 " The following Credentials are expiring on the dates shown. Failure to act before the expiration date "
105 + "will cause your App's Authentications to fail.\n");
106 email.signature("Sincerely,\nAAF Team (Our MOTS# 22830)\n"
107 + "https://wiki.web.att.com/display/aaf/Contact+Us\n"
108 + "(Use 'Other Misc Requests (TOPS)')");
110 Cred.load(trans, session,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
111 Notification.load(trans, session, Notification.v2_0_18);
112 UserRole.load(trans, session, UserRole.v2_0_11);
114 ps = new PrintStream(new FileOutputStream(logDir() + "/email"+Chrono.dateOnlyStamp()+".log",true));
115 ps.printf("### Approval Notify %s for %s%s\n",Chrono.dateTime(),batchEnv,dryRun?", DryRun":"");
119 protected void run(AuthzTrans trans) {
121 EmailWarnings ewp = org.emailWarningPolicy();
122 long now = System.currentTimeMillis();
123 Date early = new Date(now+(ewp.credExpirationWarning()*2)); // 2 months back
124 Date must = new Date(now+ewp.credExpirationWarning()); // 1 months back
125 Date critical = new Date(now+ewp.emailUrgentWarning()); // 1 week
126 Date within2Weeks = new Date(now+604800000 * 2);
127 Date withinLastWeek = new Date(now-604800000);
128 Date tooLate = new Date(now);
131 Map<String,Cred> lastCred = new HashMap<>();
132 Map<String,List<LastCred>> ownerCreds = new TreeMap<>();
136 List<LastCred> noOwner = new ArrayList<>();
137 ownerCreds.put(UNKNOWN_ID,noOwner);
139 // Get a list of ONLY the ones needing email by Owner
140 for (Entry<String, List<Cred>> es : Cred.byNS.entrySet()) {
142 for (Cred c : es.getValue()) {
143 last = c.last(CredDAO.BASIC_AUTH,CredDAO.BASIC_AUTH_SHA256);
144 if (last!=null && last.after(tooLate) && last.before(early)) {
145 List<UserRole> ownerURList = UserRole.getByRole().get(es.getKey()+".owner");
146 if (ownerURList!=null) {
147 for (UserRole ur:ownerURList) {
148 String owner = ur.user();
149 List<LastCred> llc = ownerCreds.get(owner);
151 ownerCreds.put(owner, (llc=new ArrayList<>()));
153 llc.add(new LastCred(c,last));
156 noOwner.add(new LastCred(c,last));
162 boolean bCritical,bNormal,bEarly;
164 Message msg = new Message();
165 Notification ownNotf;
166 StringBuilder logMessage = new StringBuilder();
167 for (Entry<String,List<LastCred>> es : ownerCreds.entrySet()) {
168 String owner = es.getKey();
169 boolean header = true;
171 Organization org = OrganizationFactory.obtain(env, owner);
172 Identity user = org.getIdentity(noAvg, owner);
173 if (!UNKNOWN_ID.equals(owner) && user==null) {
174 ps.printf("Invalid Identity: %s\n", owner);
176 logMessage.setLength(0);
177 if (maxEmails>emailCount) {
178 bCritical=bNormal=bEarly = false;
181 email.addTo(user==null?supportEmailAddr:user.email());
183 ownNotf = Notification.get(es.getKey(),TYPE.CN);
185 ownNotf = Notification.create(user==null?UNKNOWN_ID:user.fullID(), TYPE.CN);
188 // Get Max ID size for formatting purposes
189 int length = AAF_INSTANTIATED_MECHID.length();
190 for (LastCred lc : es.getValue()) {
191 length = Math.max(length, lc.cred.id.length());
193 String id_exp_fmt = "\t%-"+length+"s %15s %s";
195 Collections.sort(es.getValue(),LastCred.COMPARE);
196 for (LastCred lc : es.getValue()) {
197 if (lc.last.after(must) && lc.last.before(early) &&
198 (ownNotf.last==null || ownNotf.last.before(withinLastWeek))) {
199 if (!bEarly && header) {
200 msg.line("\tThe following are friendly 2 month reminders, just in case you need to schedule your updates early. "
201 + "You will be reminded next month\n");
202 msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
203 msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
207 } else if (lc.last.after(critical) && lc.last.before(must) &&
208 (ownNotf.last==null || ownNotf.last.before(withinLastWeek))) {
210 boolean last2wks = lc.last.before(within2Weeks);
213 Identity supvsr = user.responsibleTo();
214 email.addCC(supvsr.email());
215 } catch (OrganizationException e) {
216 trans.error().log(e, "Supervisor cannot be looked up");
220 msg.line("\tIt is now important for you to update Passwords all all configurations using them for the following.\n" +
221 (last2wks?"\tNote: Your Supervisor is CCd\n":"\tNote: Your Supervisor will be notified if this is not being done before the last 2 weeks\n"));
222 msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
223 msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
228 } else if (lc.last.after(tooLate) && lc.last.before(critical)) { // Email Every Day, with Supervisor
229 if (!bCritical && header) {
230 msg.line("\t!!! WARNING: These Credentials will expire in LESS THAN ONE WEEK !!!!\n" +
231 "\tYour supervisor is added to this Email\n");
232 msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
233 msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
239 Identity supvsr = user.responsibleTo();
241 email.addCC(supvsr.email());
242 supvsr = supvsr.responsibleTo();
244 email.addCC(supvsr.email());
248 } catch (OrganizationException e) {
249 trans.error().log(e, "Supervisor cannot be looked up");
252 if (bEarly || bNormal || bCritical) {
253 if (logMessage.length()==0) {
254 logMessage.append("NotifyCredExpiring");
256 logMessage.append("\n\t");
257 logMessage.append(lc.cred.id);
258 logMessage.append('\t');
259 logMessage.append(Chrono.dateOnlyStamp(lc.last));
260 msg.line(id_exp_fmt, lc.cred.id, Chrono.dateOnlyStamp(lc.last)+" ",env.getProperty(GUI_URL)+"/creddetail?ns="+Question.domain2ns(lc.cred.id));
264 if (bEarly || bNormal || bCritical) {
266 msg.line("Why are you receiving this Notification?\n");
267 msg.line("You are the listed owner of one or more AAF Namespaces. ASPR requires that those responsible for "
268 + "applications and their access review them regularly for accuracy. The AAF WIKI page for AT&T is https://wiki.web.att.com/display/aaf. "
269 + "You might like https://wiki.web.att.com/display/aaf/AAF+in+a+Nutshell. More detailed info regarding questions of being a Namespace Owner is available at https://wiki.web.att.com/pages/viewpage.action?pageId=594741363\n");
270 msg.line("You may view the Namespaces you listed as Owner for in this AAF Env by viewing the following webpage:\n");
271 msg.line(" %s/ns\n\n",env.getProperty(GUI_URL));
273 Result<Void> rv = email.exec(trans, org,"");
277 ownNotf.update(noAvg, session, false);
278 // SET LastNotification
280 email.log(ps,logMessage.toString());
282 trans.error().log(rv.errorString());
287 } catch (OrganizationException e) {
291 trans.info().printf("%d emails sent for %s", emailCount,batchEnv);
294 private static class LastCred {
298 public LastCred(Cred cred, Date last) {
303 // Reverse Sort (Oldest on top)
304 public static Comparator<LastCred> COMPARE = new Comparator<LastCred>() {
306 public int compare(LastCred o1, LastCred o2) {
307 return o2.last.compareTo(o1.last);
311 public String toString() {
312 return Chrono.dateTime(last) + cred.toString();
317 protected void _close(AuthzTrans trans) {