cbc3ef3868034d4847e17af0db24c8e381d6528b
[aaf/authz.git] / auth / auth-batch / src / main / java / org / onap / aaf / auth / update / NotifyCredExpiring.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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====================================================
19  *
20  */
21
22 package org.onap.aaf.auth.update;
23
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;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.TreeMap;
36
37 import org.onap.aaf.auth.Batch;
38 import org.onap.aaf.auth.BatchPrincipal;
39 import org.onap.aaf.auth.actions.Email;
40 import org.onap.aaf.auth.actions.EmailPrint;
41 import org.onap.aaf.auth.actions.Message;
42 import org.onap.aaf.auth.dao.cass.CredDAO;
43 import org.onap.aaf.auth.dao.hl.Question;
44 import org.onap.aaf.auth.env.AuthzTrans;
45 import org.onap.aaf.auth.helpers.Cred;
46 import org.onap.aaf.auth.helpers.Notification;
47 import org.onap.aaf.auth.helpers.Notification.TYPE;
48 import org.onap.aaf.auth.helpers.UserRole;
49 import org.onap.aaf.auth.layer.Result;
50 import org.onap.aaf.auth.org.EmailWarnings;
51 import org.onap.aaf.auth.org.Organization;
52 import org.onap.aaf.auth.org.Organization.Identity;
53 import org.onap.aaf.auth.org.OrganizationException;
54 import org.onap.aaf.auth.org.OrganizationFactory;
55 import org.onap.aaf.misc.env.APIException;
56 import org.onap.aaf.misc.env.Env;
57 import org.onap.aaf.misc.env.TimeTaken;
58 import org.onap.aaf.misc.env.util.Chrono;
59
60
61 public class NotifyCredExpiring extends Batch {
62
63     private static final String UNKNOWN_ID = "unknown@deprecated.id";
64     private static final String AAF_INSTANTIATED_MECHID = "AAF INSTANTIATED MECHID";
65     private static final String EXPIRATION_DATE = "EXPIRATION DATE";
66     private static final String QUICK_LINK = "QUICK LINK TO UPDATE PAGE";
67     private static final String DASH_1 = "-----------------------";
68     private static final String DASH_2 = "---------------";
69     private static final String DASH_3 = "----------------------------------------------------";
70     private static final String LINE = "\n----------------------------------------------------------------";
71     private Email email;
72     private int maxEmails;
73     private final PrintStream ps;
74     private final AuthzTrans noAvg;
75     private String supportEmailAddr;
76
77     public NotifyCredExpiring(AuthzTrans trans) throws APIException, IOException, OrganizationException {
78         super(trans.env());
79         TimeTaken tt = trans.start("Connect to Cluster", Env.REMOTE);
80         try {
81             session = cluster.connect();
82         } finally {
83             tt.done();
84         }
85         
86         noAvg = env.newTransNoAvg();
87         noAvg.setUser(new BatchPrincipal("batch:NotifyCredExpiring"));
88         
89         if ((supportEmailAddr = env.getProperty("mailFromUserId"))==null) {
90             throw new APIException("mailFromUserId property must be set");
91         }
92         if (isDryRun()) {
93             email = new EmailPrint();
94             maxEmails=3;
95             maxEmails = Integer.parseInt(trans.getProperty("MAX_EMAILS","3"));
96         } else {
97             email = new Email();
98             maxEmails = Integer.parseInt(trans.getProperty("MAX_EMAILS","3"));
99         }
100         
101         email.subject("AAF Password Expiration Notification (ENV: %s)",batchEnv);
102         email.preamble("AAF (MOTS 22830) is the AT&T Authorization System used by many AT&T Tools and Applications.\n\n" +
103                 "  The following Credentials are expiring on the dates shown. Failure to act before the expiration date "
104                 + "will cause your App's Authentications to fail.\n");
105         email.signature("Sincerely,\nAAF Team (Our MOTS# 22830)\n"
106                 + "https://wiki.web.att.com/display/aaf/Contact+Us\n"
107                 + "(Use 'Other Misc Requests (TOPS)')");
108
109         Cred.load(trans, session,CredDAO.BASIC_AUTH, CredDAO.BASIC_AUTH_SHA256);
110         Notification.load(trans, session, Notification.v2_0_18);
111         UserRole.load(trans, session, UserRole.v2_0_11, new UserRole.DataLoadVisitor());
112         
113         ps = new PrintStream(new FileOutputStream(logDir() + "/email"+Chrono.dateOnlyStamp()+".log",true));
114         ps.printf("### Approval Notify %s for %s%s\n",Chrono.dateTime(),batchEnv,dryRun?", DryRun":"");
115     }
116     
117     @Override
118     protected void run(AuthzTrans trans) {
119         
120         EmailWarnings ewp = org.emailWarningPolicy();
121         long now = System.currentTimeMillis();
122         Date early = new Date(now+(ewp.credExpirationWarning()*2)); // 2 months back
123         Date must = new Date(now+ewp.credExpirationWarning()); // 1 months back
124         Date critical = new Date(now+ewp.emailUrgentWarning()); // 1 week
125         Date within2Weeks = new Date(now+604800000 * 2);
126         Date withinLastWeek = new Date(now-604800000);
127         Date tooLate = new Date(now);
128         
129         // Temp structures
130         Map<String,Cred> lastCred = new HashMap<>();
131         Map<String,List<LastCred>> ownerCreds = new TreeMap<>();
132         Date last;
133         
134
135         List<LastCred> noOwner = new ArrayList<>();
136         ownerCreds.put(UNKNOWN_ID,noOwner);
137
138         // Get a list of ONLY the ones needing email by Owner
139         for (Entry<String, List<Cred>> es : Cred.byNS.entrySet()) {
140             lastCred.clear();
141             for (Cred c : es.getValue()) {
142                 last = c.last(CredDAO.BASIC_AUTH,CredDAO.BASIC_AUTH_SHA256);
143                 if (last!=null && last.after(tooLate) && last.before(early)) {
144                     List<UserRole> ownerURList = UserRole.getByRole().get(es.getKey()+".owner");
145                     if (ownerURList!=null) {
146                         for (UserRole ur:ownerURList) {
147                             String owner = ur.user();
148                             List<LastCred> llc = ownerCreds.get(owner);
149                             if (llc==null) {
150                                 ownerCreds.put(owner, (llc=new ArrayList<>()));
151                             }
152                             llc.add(new LastCred(c,last));
153                         }
154                     } else {
155                         noOwner.add(new LastCred(c,last));
156                     }
157                 }
158             }
159         }
160         
161         boolean bCritical,bNormal,bEarly;
162         int emailCount=0;
163         Message msg = new Message();
164         Notification ownNotf;
165         StringBuilder logMessage = new StringBuilder();
166         for (Entry<String,List<LastCred>> es : ownerCreds.entrySet()) {
167             String owner = es.getKey();
168             boolean header = true;
169             try {
170                 Organization org = OrganizationFactory.obtain(env, owner);
171                 Identity user = org.getIdentity(noAvg, owner);
172                 if (!UNKNOWN_ID.equals(owner) && user==null) {
173                     ps.printf("Invalid Identity: %s\n", owner);
174                 } else {
175                     logMessage.setLength(0);
176                     if (maxEmails>emailCount) {
177                         bCritical=bNormal=bEarly = false;
178                         email.clear();
179                         msg.clear();
180                         email.addTo(user==null?supportEmailAddr:user.email());
181
182                         ownNotf = Notification.get(es.getKey(),TYPE.CN);
183                         if (ownNotf==null) {
184                             ownNotf = Notification.create(user==null?UNKNOWN_ID:user.fullID(), TYPE.CN);
185                         }
186                         last = ownNotf.last;
187                         // Get Max ID size for formatting purposes
188                         int length = AAF_INSTANTIATED_MECHID.length();
189                         for (LastCred lc : es.getValue()) {
190                             length = Math.max(length, lc.cred.id.length());
191                         }
192                         String id_exp_fmt = "\t%-"+length+"s  %15s  %s";
193
194                         Collections.sort(es.getValue(),LastCred.COMPARE);
195                         for (LastCred lc : es.getValue()) {
196                             if (lc.last.after(must) && lc.last.before(early) && 
197                                 (ownNotf.last==null || ownNotf.last.before(withinLastWeek))) {
198                                 if (!bEarly && header) {
199                                     msg.line("\tThe following are friendly 2 month reminders, just in case you need to schedule your updates early.  "
200                                             + "You will be reminded next month\n");
201                                     msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
202                                     msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
203                                     header = false;
204                                 }
205                                 bEarly = true;
206                             } else if (lc.last.after(critical) && lc.last.before(must) && 
207                                     (ownNotf.last==null || ownNotf.last.before(withinLastWeek))) {
208                                 if (!bNormal) {
209                                     boolean last2wks = lc.last.before(within2Weeks);
210                                     if (last2wks) {
211                                         try {
212                                             Identity supvsr = user.responsibleTo();
213                                             email.addCC(supvsr.email());
214                                         } catch (OrganizationException e) {
215                                             trans.error().log(e, "Supervisor cannot be looked up");
216                                         }
217                                     }
218                                     if (header) {
219                                         msg.line("\tIt is now important for you to update Passwords all all configurations using them for the following.\n" +
220                                                 (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"));
221                                         msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
222                                         msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
223                                     }
224                                     header = false;
225                                 }
226                                 bNormal=true;
227                             } else if (lc.last.after(tooLate) && lc.last.before(critical)) { // Email Every Day, with Supervisor
228                                 if (!bCritical && header) {
229                                     msg.line("\t!!! WARNING: These Credentials will expire in LESS THAN ONE WEEK !!!!\n" +
230                                              "\tYour supervisor is added to this Email\n");
231                                     msg.line(id_exp_fmt, AAF_INSTANTIATED_MECHID,EXPIRATION_DATE, QUICK_LINK);
232                                     msg.line(id_exp_fmt, DASH_1, DASH_2, DASH_3);
233                                     header = false;
234                                 }
235                                 bCritical = true;
236                                 try {
237                                     if (user!=null) {
238                                         Identity supvsr = user.responsibleTo();
239                                         if (supvsr!=null) {
240                                             email.addCC(supvsr.email());
241                                             supvsr = supvsr.responsibleTo();
242                                             if (supvsr!=null) {
243                                                 email.addCC(supvsr.email());
244                                             }
245                                         }
246                                     }
247                                 } catch (OrganizationException e) {
248                                     trans.error().log(e, "Supervisor cannot be looked up");
249                                 }
250                             }
251                             if (bEarly || bNormal || bCritical) {
252                                 if (logMessage.length()==0) {
253                                     logMessage.append("NotifyCredExpiring");
254                                 }
255                                 logMessage.append("\n\t");
256                                 logMessage.append(lc.cred.id);
257                                 logMessage.append('\t');
258                                 logMessage.append(Chrono.dateOnlyStamp(lc.last));
259                                 msg.line(id_exp_fmt, lc.cred.id, Chrono.dateOnlyStamp(lc.last)+"     ",env.getProperty(GUI_URL)+"/creddetail?ns="+Question.domain2ns(lc.cred.id));
260                             }
261                         }
262                         
263                         if (bEarly || bNormal || bCritical) {
264                             msg.line(LINE);
265                             msg.line("Why are you receiving this Notification?\n");
266                                 msg.line("You are the listed owner of one or more AAF Namespaces. ASPR requires that those responsible for "
267                                         + "applications and their access review them regularly for accuracy.  The AAF WIKI page for AT&T is https://wiki.web.att.com/display/aaf.  "
268                                         + "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");
269                                 msg.line("You may view the Namespaces you listed as Owner for in this AAF Env by viewing the following webpage:\n");
270                                 msg.line("   %s/ns\n\n",env.getProperty(GUI_URL));
271                             email.msg(msg);
272                             Result<Void> rv = email.exec(trans, org,"");
273                             if (rv.isOK()) {
274                                 ++emailCount;
275                                 if (!isDryRun()) {
276                                     ownNotf.update(noAvg, session, false);
277                                     // SET LastNotification
278                                 }
279                                 email.log(ps,logMessage.toString());
280                             } else {
281                                 trans.error().log(rv.errorString());
282                             }
283                         }
284                     }
285                 }
286             } catch (OrganizationException e) {
287                 trans.info().log(e);
288             }
289         }
290         trans.info().printf("%d emails sent for %s", emailCount,batchEnv);
291     }
292     
293     private static class LastCred {
294         public Cred cred; 
295         public Date last;
296         
297         public LastCred(Cred cred, Date last) {
298             this.cred = cred;
299             this.last = last;
300         }
301         
302         // Reverse Sort (Oldest on top)
303         public static Comparator<LastCred> COMPARE = new Comparator<LastCred>() {
304             @Override
305             public int compare(LastCred o1, LastCred o2) {
306                 return o2.last.compareTo(o1.last);
307             }
308         };
309         
310         public String toString() {
311             return Chrono.dateTime(last) + cred.toString();
312         }
313     }
314     
315     @Override
316     protected void _close(AuthzTrans trans) {
317         session.close();
318         ps.close();
319     }
320 }